我正在使用以下模式:
lazy var hostingVC = UIHostingController(rootView: EDWrapperView(exportDestinationVC: self))
override func viewDidLoad() {
super.viewDidLoad()
self.addChild(hostingVC)
self.view.addSubview(hostingVC.view)
hostingVC.view.translatesAutoresizingMaskIntoConstraints = false
hostingVC.view.backgroundColor = .systemGroupedBackground
self.view.topAnchor.constraint(equalTo: hostingVC.view.topAnchor).isActive = true
self.view.bottomAnchor.constraint(equalTo: hostingVC.view.bottomAnchor).isActive = true
self.view.leadingAnchor.constraint(equalTo: hostingVC.view.leadingAnchor).isActive = true
self.view.trailingAnchor.constraint(equalTo: hostingVC.view.trailingAnchor).isActive = true
configureNavigation()
// Theme
ThemeManager.shared.registerForThemeChanges(observer: self)
}
您可以看到 SwiftUIView 引用了 UIKitVC 来调用函数(它有点像 ViewModel)。
在同一个 UIViewController 中,我有一个呈现弹出窗口的函数,它需要一个 sourceView 和一个 sourceRect。
func showActivityVC(sourceRect: CGRect) {
let activityVC = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
if let popOver = activityVC.popoverPresentationController {
popOver.sourceView = self.view! // Pass the UIVC view as sourceView
popOver.sourceRect = sourceRect // Source rect has been computed by GeometryReader in SwiftUI. But its not exactly at the right position
}
}
在 SwiftUIView 中,按钮如下所示:
List {
// Export button to iOS Share sset
Section(header: Text("Export")) {
GeometryReader { geometry in
Button(action: {
let insideFrame = geometry.frame(in: .global) // Get rect in the global view, which should be the same as the UIViewController view
self.exportDestinationVC.presentIOSShareSheet(sourceRect: insideFrame)
}, label: {
HStack(alignment: .firstTextBaseline) {
Spacer()
Image(systemName: "square.and.arrow.up")
Text("Export")
Spacer()
}
})
.contentShape(Rectangle())
.position(x: geometry.size.width/2.0, y: geometry.size.height/2.0)
}
}
}
我使用
GeometryReader
来获取全局视图中按钮的框架,它应该与UIViewController视图相同。
它有效,但不完全正确,弹出窗口没有准确指向它应该指向的位置,有一些不准确的地方,我不知道来自哪里。
如何获取 SwiftUI 视图中 SwiftUI 按钮的确切 sourceRect?
我也遇到了同样的问题,几何阅读器没有提供按钮的正确 y 值,并且弹出窗口在方向更改期间飞走了。
所以,我做了以下事情: 我为 UIbutton 创建了一个 UIViewRepresentable(比如 MyButton)实例,制作了一个保存 UIbutton 的自定义协调器。
MyButton 有一个回调((UIButton) -> Void),当点击 MyButton 时,我在该回调中从协调器返回了 UIbutton,并将其设置为我的弹出窗口的 sourceView,然后呈现它,它就像一个魅力。
struct MyButton: UIViewRepresentable {
var action: (UIButton) -> Void
func makeUIView(context: Self.Context) -> UIButton {
let uiButton = UIButton()
context.coordinator.uiButton = uiButton
context.coordinator.addTarget()
return uiButton
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func updateUIView(_ uiView: UIButton, context: Self.Context) {}
class Coordinator: NSObject {
var parent: MyButton
var uiButton = UIButton()
init(_ uiView: MyButton) {
self.parent = uiView
}
func addTarget() {
uiButton.addTarget(self, action: #selector(tapped), for: .touchUpInside)
}
@objc func tapped() {
self.parent.action(uiButton)
}
}
}
这是一个更简单的工作解决方案,不需要协调员。同时,它允许动态设置标题、字体和更改颜色。
struct ButtonWithSourceView: UIViewRepresentable {
var title: String
var font: UIFont
@Binding var color: UIColor
var action: (UIButton) -> Void
func makeUIView(context: Self.Context) -> UIButton {
let uiButton = UIButton()
uiButton.setTitle(title, for: .normal)
uiButton.titleLabel?.font = font
uiButton.setTitleColor(color, for: .normal)
let uiAction = UIAction() { _ in
action(uiButton)
}
uiButton.addAction(uiAction, for: .touchUpInside)
return uiButton
}
func updateUIView(_ uiView: UIButton, context: Self.Context) {
uiView.setTitleColor(color, for: .normal)
}
}
对于任何遇到同样问题的人,根据@vomi的回答,就我而言,我创建了一个
UIViewRepresentable
来获取sourceView
。我通常将 UIViewRepresentable
嵌入到 ZStack
中,您希望从中获取 sourceView
来呈现弹出窗口。它运行完美。
struct CustomViewWithSourceView: UIViewRepresentable {
var onAppear: (UIView) -> Void
func makeUIView(context: Self.Context) -> UIView {
let uiView = UIView()
DispatchQueue.main.async {
self.onAppear(uiView)
}
return uiView
}
func updateUIView(_ uiView: UIView, context: Self.Context) {
DispatchQueue.main.async {
self.onAppear(uiView)
}
}
}