【问题标题】:SwiftUI @State var initialization issueSwiftUI @State var 初始化问题
【发布时间】:2019-11-03 14:35:21
【问题描述】:

我想通过Struct 的init() 方法在SwiftUI 中初始化@State var 的值,以便它可以从准备好的字典中获取正确的文本,以便在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)
    }
}

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

我该如何解决这种情况?非常感谢您!

【问题讨论】:

  • 使用State(initialValue:)
  • @Daniel 请在第二个数字上以 150+ 作为已接受的答案。和我一样,很多人都错过了第二个答案,并且卡住了很长时间。
  • most upvoted answer 可能是您想要的答案,而不是接受的答案。

标签: swiftui


【解决方案1】:

SwiftUI 不允许您在初始化程序中更改 @State,但您可以初始化它。

去掉默认值,直接使用_fullText设置@State,而不是通过属性包装访问器。

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

init(letter: String) {
    _fullText = State(initialValue: list[letter]!)
}

【讨论】:

  • 这应该是公认的答案,在某些情况下 onAppear 可能为时已晚。
  • 同意。这应该是公认的答案。我遇到了一个问题,只要我可以从 Picker 视图返回,属性值就会被重置。
  • @Diesel 起初我犹豫要不要使用它,因为我认为它正在访问一些内部 API,但事实证明它是 Swift 语言的一部分。有关属性包装器如何在后台工作的简明信息,包括合成下划线 (_),请参阅 propertyWrapper 下的 docs.swift.org/swift-book/ReferenceManual/Attributes.html
  • 您还应该注意,您在init 中初始化的状态可能会在所有存储的属性初始化后立即被覆盖。请参阅this comment,其中  工程师明确表示不要使用 init 初始化 @State 而是内联。
【解决方案2】:

我会尝试在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]!
             }
    }
}

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


更新:BindableObject 现在称为 ObservableObject。

【讨论】:

  • 谢谢,这非常有效(尽管我已经在 TextField 周围的 VStack 上应用了 .onAppear)。
  • 破解时间稍长我注意到您的解决方案是这种情况的解决方法,但是我遇到了更多必须初始化@State var的情况。虽然也可以在那里应用这种方法,但我认为这不是处理它的最佳方法。
  • @DanielMessner :是的,我同意,对于更复杂的情况,只需使用模型对象 (BindableObject) 并在该对象中进行初始化,无论是在运行 init() 时还是在视图中由.onAppear 事件。
  • 这不是问题中特定情况的最佳解决方案 - 但它是另一种类型问题的最佳解决方案。我有一个@State,其初始值取决于我传入的另一个对象,因此每次显示视图时,我都希望它具有不同的初始值。另一个答案不能解决这个问题,但是在 onAppear 中设置状态可以。
【解决方案3】:

Bogdan Farca 的答案对于这种情况是正确的,但我们不能说这是所提问题的解决方案,因为我发现所提问题中的 Textfield 存在问题。我们仍然可以将 init 用于相同的代码所以查看下面的代码,它显示了所提问题的确切解决方案。

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 {
        VStack {
            Text("\(self.fullText)")
            TextField("Enter some text", text: $fullText)
        }
    }
}

只需在视图中调用即可使用它

struct ContentView: View {
    var body: some View {
        StateFromOutside(letter: "a")
    }
}

【讨论】:

    【解决方案4】:

    要使用@State,你必须导入SwiftUI,所以写

    import SwiftUI
    

    在代码的顶部。

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。
    【解决方案5】:

    请参阅下面示例中的.id(count)。

    import SwiftUI
    import MapKit
    
    struct ContentView: View {
    @State private var count = 0
    
    var body: some View {
        Button("Tap me") {
            self.count += 1
            print(count)
        }
        Spacer()
        testView(count: count).id(count) // <------ THIS IS IMPORTANT. Without this "id" the initializer setting affects the testView only once and calling testView again won't change it (not desirable, of course)
    }
    }
    
    
    
    struct testView: View {
    var count2: Int
    @State private var region: MKCoordinateRegion
    
    init(count: Int) {
        count2 = 2*count
        print("in testView: \(count)")
        
        let lon =  -0.1246402 + Double(count) / 100.0
        let lat =  51.50007773 + Double(count) / 100.0
        let myRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: lat, longitude: lon) , span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
        _region = State(initialValue: myRegion)
    }
    
    var body: some View {
        Map(coordinateRegion: $region, interactionModes: MapInteractionModes.all)
        Text("\(count2)")
    }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-10
      • 1970-01-01
      • 1970-01-01
      • 2020-10-29
      • 2021-10-28
      • 1970-01-01
      相关资源
      最近更新 更多