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
相关产品推荐
相关产品推荐

