SwiftUI中iPad Popover分享面板位置异常的解决求助
解决iPad上SwiftUI唤起Popover分享面板位置异常问题
问题原因
你当前代码的核心问题是:创建了一个未添加到视图层级的UIView作为sourceView。系统无法识别这个孤立视图的实际位置,因此Popover会默认显示在屏幕左上角。
解决方案
推荐两种可靠的实现方式:
方式一:利用UIViewControllerRepresentable封装分享功能(更符合SwiftUI范式)
通过自定义UIViewControllerRepresentable组件,直接在SwiftUI视图中集成UIActivityViewController,并正确关联源视图:
struct ShareSheet: UIViewControllerRepresentable { let items: [Any] let sourceRect: CGRect let sourceView: UIView func makeUIViewController(context: Context) -> UIActivityViewController { let controller = UIActivityViewController(activityItems: items, applicationActivities: nil) controller.popoverPresentationController?.sourceView = sourceView controller.popoverPresentationController?.sourceRect = sourceRect return controller } func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {} } struct ContentView: View { @State private var showShareSheet = false @State private var buttonFrame: CGRect = .zero var body: some View { VStack { Spacer(minLength: 900) Button("Share") { showShareSheet = true } .padding() .background(Color.pink) .background( GeometryReader { geo in Color.clear.onAppear { buttonFrame = geo.frame(in: .global) } } ) Text("Button Frame: \(buttonFrame.debugDescription)") .padding() .background(Color.mint) .lineLimit(nil) } .background(Color.yellow) .sheet(isPresented: $showShareSheet) { guard let sourceView = UIApplication.shared.windows.first?.rootViewController?.view else { return } ShareSheet(items: ["Some Text to Share"], sourceRect: buttonFrame, sourceView: sourceView) } } }
方式二:修正原有代码,使用已存在的视图作为sourceView
如果你想保留原有代码结构,需要将sourceView设置为已经存在于视图层级中的UIView(比如根视图的view),并将sourceRect设置为按钮在该视图坐标系下的位置:
struct ContentView: View { @State var label = "N/A" @State var buttonPos = CGRect(x: 0, y: 0, width: 0, height: 0) var body: some View { VStack{ Spacer(minLength: 900) GeometryReader { geometry in Button("Share"){ showShareSheetAtPopover() } .padding() .frame(width: geometry.size.width, height: geometry.size.height) .background(Color.pink) .onAppear { buttonPos = geometry.frame(in: .global) let localPos = geometry.frame(in: .local) label = "Width: \(buttonPos.size) \n Global Pos: \(buttonPos.origin) \n Local Pos: \(localPos.origin)" } } .background(Color.orange) Text(label) .padding() .background(Color.mint) .frame(minHeight: 0, maxHeight: .infinity) .lineLimit(nil) .fixedSize(horizontal: false, vertical: true) } .background(Color.yellow) } func showShareSheetAtPopover(){ guard let rootVC = UIApplication.shared.windows.first?.rootViewController else { return } let shareItem = ["Some Text to Share"] let ac = UIActivityViewController(activityItems: shareItem, applicationActivities: nil) guard let ppc = ac.popoverPresentationController else { rootVC.present(ac, animated: true) return } // 使用根视图的view作为sourceView ppc.sourceView = rootVC.view // 将按钮的全局坐标转换为根视图坐标系下的rect ppc.sourceRect = rootVC.view.convert(buttonPos, from: nil) rootVC.present(ac, animated: true) } }
关键要点
- 必须确保
sourceView是已添加到视图层级中的有效UIView,不能使用新建的孤立视图。 sourceRect需要是相对于sourceView坐标系的矩形,若使用全局坐标,需通过convert(_:from:)转换到对应视图的坐标系。
内容的提问来源于stack exchange,提问作者Zimm3r
相关产品推荐
相关产品推荐

