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

SwiftUI使用ShareSheet出现控制台报错及iOS15适配问题

报错处理说明

你遇到的三个控制台日志都是iOS系统内部UIActivityViewController的实现问题,和你的业务代码无关,不需要额外处理,也不会影响App上线和功能正常运行:

  • 第一个LayoutConstraints相关警告:是系统自己修改了UICollectionView管理的复用视图的自动布局属性触发的日志,社区大量开发者都遇到过该问题,苹果在后续iOS版本中已逐步修复这类日志输出。
  • 第二个imageForImageDescriptor警告:是系统加载分享图标时内部调用了废弃方法产生的日志,属于系统级问题,无需业务侧处理。
  • 第三个UICollectionViewRecursion日志:只是系统告知关闭了集合视图的递归触发日志打印,完全不影响功能逻辑。

实现方案优化建议

你选择用UIViewControllerRepresentable封装ShareSheet的思路是完全正确的,比直接获取rootViewController弹出的方式更符合SwiftUI的声明式开发规范,也天然避免了操作UIApplication.shared.windows的废弃问题,可从以下几个点做优化:

  1. 先修正你现有代码的语法错误:ListDataArray的dot数组中,DataArray初始化的cities参数后缺少逗号,且第二个DataArray的cities参数后多了右括号,修正后才能正常编译运行。
  2. 优先考虑系统原生方案:如果你的App最低支持iOS 16+,直接使用SwiftUI官方提供的ShareLink组件即可,不需要自己封装任何UIKit组件,代码最简也不会产生额外兼容问题。
  3. 低版本兼容优化:如果需要兼容iOS 15及更低版本,只需在你现有的ShareSheet封装中补充iPad端的popover适配,避免iPad上出现显示异常或闪退问题。

优化后代码示例

语法错误修正

public struct ListDataArray {
    static let dot = [
        DataArray(
            id: 1,
            cities: "Baltimore",
            name1: "John",
            name2: "Mike",
            isFavorite: false
        ),
        DataArray(
            id: 2,
            cities: "Frederick",
            name1: "Joe",
            name2: "Swift",
            isFavorite: false
        ),
        DataArray(
            id: 3,
            cities: "Catonsville",
            name1: "Susan",
            name2: "Oliver",
            isFavorite: false
        )
    ]
}

iOS 16+ 原生ShareLink实现

struct TestShareSheet3: View {
    var data: DataArray
    
    var body: some View {
        VStack {
            ShareLink(
                item: "\(data.cities)\n\(data.name1) - \(data.name2)",
                label: {
                    Text("Share")
                        .foregroundColor(.blue)
                        .font(.title3)
                        .padding()
                        .background(Color.green)
                        .cornerRadius(10)
                }
            )
        }
    }
}

iOS 15+ 兼容版ShareSheet实现

// 优化后的ShareSheet封装
struct ShareSheet: UIViewControllerRepresentable {
    typealias Callback = (_ activityType: UIActivity.ActivityType?, _ completed: Bool, _ returnedItems: [Any]?, _ error: Error?) -> Void
    
    let activityItems: [Any]
    var applicationActivities: [UIActivity]? = nil
    var excludedActivityTypes: [UIActivity.ActivityType]? = nil
    var callback: Callback? = nil
    // 传入分享按钮的全局坐标,用于iPad端popover定位
    var sourceRect: CGRect = .zero
    
    func makeUIViewController(context: Context) -> UIActivityViewController {
        let controller = UIActivityViewController(
            activityItems: activityItems,
            applicationActivities: applicationActivities
        )
        controller.excludedActivityTypes = excludedActivityTypes
        controller.completionWithItemsHandler = callback
        
        // 适配iPad端弹出位置
        if let popover = controller.popoverPresentationController,
           let rootView = UIApplication.shared.connectedScenes
            .filter({ $0.activationState == .foregroundActive })
            .compactMap({ $0 as? UIWindowScene })
            .first?.windows.first(where: \.isKeyWindow)?.rootViewController?.view {
            popover.sourceView = rootView
            popover.sourceRect = sourceRect
        }
        return controller
    }
    
    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}

// 调用侧代码
struct TestShareSheet3: View {
    var data: DataArray
    @State private var showShareSheet = false
    @State private var buttonFrame = CGRect.zero
    
    var body: some View {
        VStack {
            Button {
                showShareSheet.toggle()
            } label: {
                Text("Share")
                    .foregroundColor(.blue)
                    .font(.title3)
                    .padding()
                    .background(Color.green)
                    .cornerRadius(10)
            }
            // 读取按钮的全局坐标用于iPad定位
            .background(
                GeometryReader { geo in
                    Color.clear
                        .onAppear { buttonFrame = geo.frame(in: .global) }
                        .onChange(of: geo.frame(in: .global)) { newValue in
                            buttonFrame = newValue
                        }
                }
            )
            .sheet(isPresented: $showShareSheet) {
                ShareSheet(
                    activityItems: ["\(data.cities)\n\(data.name1) - \(data.name2)"],
                    sourceRect: buttonFrame
                )
                .presentationDetents([.medium, .large])
            }
        }
    }
}

全局弹出方式兼容iOS 15+

如果你确实需要使用全局弹出的方式,替换废弃的windows获取逻辑即可:

private func getRootViewController() -> UIViewController? {
    UIApplication.shared.connectedScenes
        .filter { $0.activationState == .foregroundActive }
        .compactMap { $0 as? UIWindowScene }
        .first?.windows
        .first(where: \.isKeyWindow)?
        .rootViewController
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 03:06:03