iOS SwiftUI SafariView首次URL加载异常问题求助
问题分析
这个问题的核心原因是你在ForEach循环内部为每个按钮都附加了一个.sheet,而所有这些sheet都绑定同一个showSafariView状态变量。当首次点击按钮时,SwiftUI会初始化对应位置的sheet,但此时safariURL的状态更新和sheet的展示存在时序竞争,导致首次弹出的sheet捕获到的是safariURL的初始nil值——即使控制台显示已经设置了正确的URL,这个已经初始化的sheet也不会自动刷新读取最新值。后续切换商品后,sheet实例被重新初始化,就能正确获取到最新的URL状态。
解决方案
把.sheet从ForEach循环内部移到整个列表的外层,只维护一个全局的sheet实例,这样就能确保每次弹出时都读取最新的safariURL状态。
修改后的代码示例
- 变量定义保持不变:
@StateObject private var dataProvider = DataProvider() @State private var safariURL: URL? = nil @State private var showSafariView: Bool = false
- 调整商品列表和按钮的代码:
将.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.") } }
- 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
相关产品推荐
相关产品推荐

