SwiftUI @State var初始化问题

Dan*_*ner 11 swiftui

我想通过a @Stateinit()方法在SwiftUI中初始化var 的值Struct,以便它可以从准备好的字典中获取适当的文本以用于TextField中的操作。源代码如下所示:

struct StateFromOutside: View {
    let list = [
        "a": "Letter A",
        "b": "Letter B",
        // ...
    ]
    @State var fullText: String = ""

    init(letter: String) {
        self.fullText = list[letter]!
    }

    var body: some View {
        TextField($fullText)
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是执行失败并显示错误 Thread 1: Fatal error: Accessing State<String> outside View.body

我该如何解决?提前非常感谢您!

Kev*_*vin 25

SwiftUI不允许您更改 @State初始值设定项,但可以对其进行初始化

删除默认值,并使用_fullText@State直接进行设置,而不用通过属性包装器访问器。

@State var fullText: String // No default value of ""

init(letter: String) {
    _fullText = State(initialValue: list[letter]!)
}
Run Code Online (Sandbox Code Playgroud)

  • 答案并不是问题的解决方案。它不会“像做梦一样”,而是噩梦般的。在这里,如果我们想改变视图中的值,我们需要使用可以在初始化程序中初始化的绑定。在 init 中初始化 @State 只会在第一次创建该视图的值时执行。后续视图不会看到在 init 中作为参数传递的值,而是看到 SwiftUI 系统缓存的先前值。 (10认同)
  • https://developer.apple.com/forums/thread/657393“init 不是更新 @State 变量值的好地方。” (6认同)
  • 这应该是公认的答案,在某些情况下onAppear可能为时已晚。 (5认同)
  • @Diesel 起初我很犹豫是否要使用它,因为我认为它正在访问一些内部 API,但事实证明它是 Swift 语言的一部分。有关属性包装器如何在后台工作(包括合成下划线 (_))的简明信息,请参阅 **propertyWrapper** 下的 https://docs.swift.org/swift-book/ReferenceManual/Attributes.html (4认同)
  • 您还应该注意,在初始化所有存储的属性后,您在“init”中初始化的状态可能会立即被覆盖。请参阅[此评论](https://forums.swift.org/t/state-messing-with-initializer-flow/25276/3),其中  工程师明确表示不要使用 `init` 初始化 @State,而是执行以下操作它是内联的。 (4认同)
  • 同意。这应该是公认的答案。我遇到了一个问题,只要我可以从选择器视图返回,属性值就会重置。 (3认同)
  • https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID617 (3认同)
  • 下划线记录在哪里? (2认同)
  • 这个解决方案很好,但并不完整。问题是对视图的后续调用对 State 变量没有影响。解决方案很简单:将“.id(some_var)”添加到父视图中的调用者中。请参阅我的回答以获取完整示例 (2认同)

Val*_*adu 20

最重要的答案是一种反模式,当依赖项发生变化 ( letter) 并且您的状态不会相应更新时,它会在以后带来痛苦。

永远不应该使用State(initialValue:)State(wrappedValue:)初始化 a 中的View状态init。事实上,State应该只内联初始化,如下所示:

@State private var fullText: String = "The value"
Run Code Online (Sandbox Code Playgroud)

如果这不可行,请使用@Binding、 、和@ObservedObject之间的组合,甚至是自定义@Binding@StateDynamicProperty

更多相关信息请参见此处


Bog*_*rca 6

我会尝试将其初始化onAppear

struct StateFromOutside: View {
    let list = [
        "a": "Letter A",
        "b": "Letter B",
        // ...
    ]
    @State var fullText: String = ""

    var body: some View {
        TextField($fullText)
             .onAppear {
                 self.fullText = list[letter]!
             }
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,甚至更好的是,使用模型对象(BindableObject链接到您的视图)并在那里执行所有初始化和业务逻辑。您的视图将更新以自动反映更改。

  • 这不被认为是问题中特定情况的最佳解决方案 - 但它是另一种类型问题的最佳解决方案。我有一个 @State ,其初始值取决于我传入的另一个对象,因此每次显示视图时,我希望它具有不同的初始值。另一个答案没有解决这个问题,但是在 onAppear 中设置状态可以解决这个问题。 (3认同)