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

SwiftUI中使用UIActivityViewController时如何设置sourceView避免崩溃

解决SwiftUI中UIActivityViewController崩溃问题

方案1:iOS16+ 用SwiftUI原生ShareLink(推荐)

如果你的App仅需支持iOS16及以上版本,直接使用SwiftUI原生组件即可,无需手动处理UIActivityViewController,它会自动适配iPhone/iPad的展示形式:

struct ContentView: View {
    let shareURL = URL(string: "https://www.example.com")!
    
    var body: some View {
        ShareLink(item: shareURL) {
            Text("分享链接")
        }
    }
}

方案2:兼容iOS13+的UIKit实现

若需兼容更低版本,需正确配置UIActivityViewController的popover展示源,并使用当前推荐的方式获取活跃视图控制器:

步骤1:添加获取当前视图控制器的工具扩展

extension UIApplication {
    func currentViewController() -> UIViewController? {
        connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
            .first { $0.isKeyWindow }?
            .rootViewController?
            .topMostViewController()
    }
}

extension UIViewController {
    func topMostViewController() -> UIViewController {
        if let presented = presentedViewController {
            return presented.topMostViewController()
        }
        return self
    }
}

步骤2:封装分享弹窗并设置源视图

通过UIViewControllerRepresentable封装分享控制器,同时获取SwiftUI按钮对应的UIView作为popover的源:

struct ShareSheet: UIViewControllerRepresentable {
    let activityItems: [Any]
    let sourceView: UIView?
    
    func makeUIViewController(context: Context) -> UIActivityViewController {
        let controller = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
        // 为popover指定弹出源,适配iPad等设备
        controller.popoverPresentationController?.sourceView = sourceView
        controller.popoverPresentationController?.sourceRect = sourceView?.bounds ?? .zero
        return controller
    }
    
    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}

struct ContentView: View {
    @State private var showShareSheet = false
    let shareURL = URL(string: "https://www.example.com")!
    @State private var shareButtonView: UIView?
    
    var body: some View {
        Button("分享链接") {
            showShareSheet = true
        }
        // 获取按钮对应的UIView引用
        .background(
            UIViewCaptureHelper { view in
                shareButtonView = view
            }
        )
        .sheet(isPresented: $showShareSheet) {
            ShareSheet(activityItems: [shareURL], sourceView: shareButtonView)
        }
    }
}

// 辅助工具:捕获SwiftUI视图对应的UIView
struct UIViewCaptureHelper: UIViewRepresentable {
    let onViewCreated: (UIView) -> Void
    
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        onViewCreated(view)
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {}
}

崩溃原因说明

在iPad等支持popover的设备上,UIActivityViewController默认以popover形式展示,必须指定sourceView或barButtonItem来定义弹出位置,未设置则会触发NSGenericException异常。同时原代码中UIApplication.shared.windows已在iOS15+废弃,需改用UIWindowScene方式获取当前窗口。

若分享按钮位于导航栏,可直接设置barButtonItem替代sourceView:

.navigationBarItems(trailing: Button(action: {
    let controller = UIActivityViewController(activityItems: [shareURL], applicationActivities: nil)
    if let popover = controller.popoverPresentationController {
        popover.barButtonItem = navigationItem.rightBarButtonItem
    }
    UIApplication.shared.currentViewController()?.present(controller, animated: true)
}) {
    Image(systemName: "square.and.arrow.up")
})

内容的提问来源于stack exchange,提问作者user291701

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 00:45:09