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

从UIViewController调用SwiftUI视图并传参的问题(含UserDefaults方案)

解决UIViewController调用SwiftUI视图时的URL传递问题

你的核心问题在于用UserDefaults传递临时URL数据的方式不合适,同时存在强制解包导致崩溃的风险。直接将URL作为参数传给SwiftUI视图是更安全高效的方案,下面是具体修改步骤:

1. 移除UserDefaults传递逻辑,直接传URL给SwiftUI视图

在UIViewController的调用代码中,不需要把URL存到UserDefaults,直接将docURL作为参数传入ProjectDocumentOpener:

// 替换原来的UserDefaults存储和跳转代码
let openerView = ProjectDocumentOpener(open: .constant(true), documentURL: docURL)
let hostingVC = UIHostingController(rootView: openerView)
navigationController?.pushViewController(hostingVC, animated: true)

2. 修改SwiftUI视图,直接接收URL参数

更新ProjectDocumentOpener,添加documentURL参数,移除所有UserDefaults相关的读取逻辑:

struct ProjectDocumentOpener: View {
    @Binding var open: Bool
    let documentURL: URL // 直接接收外部传入的URL
    @State var errorInAccess = false
    
    var body: some View {
        NavigationView {
            VStack(alignment: .center, spacing: 0) {
                PreviewController(url: documentURL, error: $errorInAccess)
            }
        }
        .navigationBarTitleDisplayMode(.inline)
        .navigationBarTitle(Text(documentURL.lastPathComponent))
        .toolbar {
            ToolbarItem(placement: .navigationBarLeading) {
                Button("Done") {
                    open = false
                }
            }
        }
    }
}

3. 优化PreviewController的安全作用域资源处理

调整Coordinator中的previewItemAt方法,简化URL处理,确保安全作用域资源的正确释放:

func previewController(
    _ controller: QLPreviewController, previewItemAt index: Int
) -> QLPreviewItem {
    // 仅当URL是安全作用域类型时,才调用资源访问方法
    let didAccess = parent.url.startAccessingSecurityScopedResource()
    defer {
        if didAccess {
            parent.url.stopAccessingSecurityScopedResource()
        }
    }
    // 直接返回URL,无需转成NSURL(fileURLWithPath:)
    return parent.url as QLPreviewItem
}

问题根源分析

  1. UserDefaults存储URL的错误:你直接调用UserDefaults.standard.set(docURL, forKey:)会将URL以plist格式存储,而用string(forKey:)读取得到的不是有效的URL字符串,导致URL(string:)返回nil。即使改用docURL.absoluteString存储,UserDefaults也不适合传递临时数据,容易出现同步问题。
  2. 强制解包的风险:代码中多处使用!强制解包,一旦UserDefaults中没有对应值,就会直接崩溃,用参数传递的方式从根源避免了这个问题。
  3. SwiftUI body的调用特性:body会被系统多次调用,在其中读取UserDefaults会导致不必要的重复操作,将数据作为初始化参数传入是更符合SwiftUI设计的做法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 08:15:54