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

iOS SwiftUI SafariView首次URL加载异常问题求助

问题分析

这个问题的核心原因是你在ForEach循环内部为每个按钮都附加了一个.sheet,而所有这些sheet都绑定同一个showSafariView状态变量。当首次点击按钮时,SwiftUI会初始化对应位置的sheet,但此时safariURL的状态更新和sheet的展示存在时序竞争,导致首次弹出的sheet捕获到的是safariURL的初始nil值——即使控制台显示已经设置了正确的URL,这个已经初始化的sheet也不会自动刷新读取最新值。后续切换商品后,sheet实例被重新初始化,就能正确获取到最新的URL状态。

解决方案

把.sheet从ForEach循环内部移到整个列表的外层,只维护一个全局的sheet实例,这样就能确保每次弹出时都读取最新的safariURL状态。

修改后的代码示例

  1. 变量定义保持不变:
@StateObject private var dataProvider = DataProvider()
@State private var safariURL: URL? = nil
@State private var showSafariView: Bool = false
  1. 调整商品列表和按钮的代码:
    将.sheet从Button的修饰器中移除,放到整个列表的外层(比如列表所在的ScrollView或VStack末尾):
ScrollView {
    ForEach(dataProvider.products) { product in
        VStack(alignment: .leading) {
            // 商品其他展示内容...
            
            // "Info" Button
            Button(action: {
                if let urlString = product.productUrl, let url = URL(string: urlString) {
                    safariURL = url
                    print("Safari URL set: \(url); \(String(describing: safariURL))")
                    showSafariView = true
                } else {
                    print("Err. 1: No valid URL available.")
                    safariURL = nil
                    showSafariView = false
                }
            }) {
                HStack {
                    Text("INFO")
                }
                .frame(width: 120, height: 10)
                .padding()
            }
            .background(Color.blueGradient)
            .foregroundColor(.white)
        }
    }
}
// 全局唯一的sheet实例
.sheet(isPresented: $showSafariView) {
    if let safariURL = safariURL {
        SafariView(url: safariURL)
    } else {
        Text("Err. 2: No valid URL to display.")
    }
}
  1. SafariView及控制器代码无需修改:
struct SafariView: View {
    var url: URL
    
    var body: some View {
        SafariViewController(url: url)
            .edgesIgnoringSafeArea(.all)
    }
}

struct SafariViewController: UIViewControllerRepresentable {
    var url: URL
    
    func makeUIViewController(context: Context) -> SFSafariViewController {
        let safariVC = SFSafariViewController(url: url)
        return safariVC
    }
    
    func updateUIViewController(_ uiViewController: SFSafariViewController, context: Context) {
        // 当前场景无需额外处理
    }
}

额外优化方案

可以使用.sheet(item:)替代布尔状态绑定,直接通过可选URL对象控制sheet展示,逻辑更简洁:

// 移除showSafariView,直接用safariURL控制弹窗
@State private var safariURL: URL? = nil

// 按钮Action简化
Button(action: {
    if let urlString = product.productUrl, let url = URL(string: urlString) {
        safariURL = url
        print("Safari URL set: \(url)")
    } else {
        print("Err. 1: No valid URL available.")
        safariURL = nil
    }
}) {
    // 按钮UI...
}

// 外层sheet改为
.sheet(item: $safariURL) { url in
    SafariView(url: url)
}

// 给URL添加Identifiable扩展(必填)
extension URL: Identifiable {
    public var id: String { absoluteString }
}

这种方式无需额外布尔变量,直接通过URL的非空状态控制弹窗,彻底规避状态时序问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 18:47:28