SwiftUI 表在第一次出现时无法访问状态变量的最新值

mar*_*cgg 14 ios swift swiftui

当第一次显示工作表时,状态变量似乎没有正确更新。

例如使用以下代码:

import SwiftUI

struct DemoView: View {
    @State var showDetails: Bool = false
    var body: some View {
        VStack {
            Button(action: {
              showDetails = true
            }) {
                Text("Show sheet")
            }
        }.sheet(isPresented: $showDetails){
            VStack {
                Text("showDetails: \(showDetails ? "yes" : "no")")
            }
        }
    }
}

struct DemoView_Previews: PreviewProvider {
    static var previews: some View {
        DemoView()
    }
}
Run Code Online (Sandbox Code Playgroud)

第一次单击时将显示“否”,第二次单击时将显示“是”,如下所示:

在此输入图像描述

我错过了什么吗?如何确保工作表视图正确读取我的状态变量?

Asp*_*eri 11

您会看到工作表创建时间的价值。如果要跟踪父视图状态,请创建绑定到该状态的工作表子视图,如下所示。当子视图出现时,绑定将更新子视图。

使用 Xcode 12 / iOS 14 进行
测试 使用 Xcode 13.3 / iOS 15.4 重新测试

struct DemoView: View {
    @State var showDetails: Bool = false
    var body: some View {
        VStack {
            Button(action: {
              showDetails = true
            }) {
                Text("Show sheet")
            }
        }.sheet(isPresented: $showDetails){
            SheetDetailView(flag: $showDetails)
        }
    }
}

struct SheetDetailView: View {
    @Binding var flag: Bool
    var body: some View {
        VStack {
            Text("showDetails: \(flag ? "yes" : "no")")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


muv*_*aaa 6

虽然这里有一些答案展示了如何缓解这个问题,但我认为它们都没有解释为什么会出现问题或为什么修复有效,在我看来,这比盲目使用修复更重要。

基本上,如果 SwiftUI在第一次评估它时访问了DemoView任何属性,它似乎会重新评估主体。@State由于showDetails属性仅在按钮操作回调中设置,并在工作表内容视图构建器回调中访问,而这些回调不在主体中执行,因此 SwiftUI 似乎不知道在点击更新主体时需要重新评估主体。

您可以通过在主体变量中添加简单的打印来检查这一点,如下所示:

struct DemoView: View {
    @State var showDetails: Bool = false
    var body: some View {
        print("Body evaluated")
        return VStack {
            Button(action: {
              showDetails = true
            }) {
                Text("Show sheet")
            }
        }.sheet(isPresented: $showDetails){
            VStack {
                Text("showDetails: \(showDetails ? "yes" : "no")")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

点击按钮后,将不会重新评估身体。这就是为什么showDetails工作表中捕获的值是初始false值的原因。

如果您要在正文showDetails中使用变量DemoView,无论是显示它还是只是添加简单的打印,您将看到正文被重新评估,并且您在工作表中看到的值是最新的。

struct DemoView: View {
    @State var showDetails: Bool = false
    var body: some View {
        print("Body evaluated \(showDetails)")
        return VStack {
            Button(action: {
              showDetails = true
            }) {
                Text("Show sheet")
            }
        }.sheet(isPresented: $showDetails){
            VStack {
                Text("showDetails: \(showDetails ? "yes" : "no")")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我想补充一点,因为我认为这是最好的答案。我遇到了一个类似的问题(直到这个答案才意识到这是相同的),malhal 在我的答案中回答了这个问题(此处:/sf/answers/5331915081/)说你可以添加 `[showDetails] in` 在工作表闭包的开头,例如 `.sheet(isPresented: $showDetails) { [showDetails] in` ,基本上我的理解是,它使视图在将其传递给闭包之前重新评估 `showDetails` ,因为它现在知道它需要它!如果您不需要双向绑定并且只想将值传递到子视图,那么感觉会更整洁。 (5认同)