You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 02:25:51