SwiftUI@State var 初始化问题

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

我怎样才能解决这个问题呢? 非常感谢你提前!

55720 次浏览

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

SwiftUI 不允许您在初始化程序中使用 改变 @State,但是您可以使用 初始化

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

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


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

对于这个问题,波格丹 · 法尔卡的回答是正确的,但是我们不能说这就是问题的解决方案,因为我发现问题中有 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")
}
}

请参阅下面示例中的 .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)")
}
}

init方法中设置 @State变量的默认值现在已经不是问题了。但是 必须的只需去掉给状态的默认值,它就会按照预期工作:

,,,
@State var fullText: String // <- No default value here


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


var body: some View {
TextField("", text: $fullText)
}
}

最上面的答案是不正确的。不应该使用 State(initialValue:)State(wrappedValue:)来初始化 Viewinit中的状态。事实上,State应该只能内联初始化,如下所示:

@State private var fullText: String = "The value"

如果这不可行,使用 @Binding@ObservedObject@Binding@State之间的组合,甚至一个定制的 DynamicProperty

在您的特定情况下,@Bindable + @State + onAppear + onChange应该可以做到这一点。

更多关于这个以及 DynamicProperty是如何工作的,给你

您可以创建一个视图模型,并启动相同的操作:

 class LetterViewModel: ObservableObject {


var fullText: String
let listTemp = [
"a": "Letter A",
"b": "Letter B",
// ...
]


init(initialLetter: String) {
fullText = listTemp[initialLetter] ?? ""
}
}


struct LetterView: View {


@State var viewmodel: LetterViewModel


var body: some View {
    

TextField("Enter text", text: $viewmodel.fullText)
}
}

然后这样称呼视图:

 struct ContentView: View {


var body: some View {


LetterView(viewmodel: LetterViewModel(initialLetter: "a"))
}
}

通过这种方式,您也不必调用 State 实例化方法。

//MARK:-物业

@State private var fullText: String

//MARK:-Init.

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

//马克:-查看

var body: some View {
TextField("", text: $fullText)
}

根据不同的情况,可以用不同的方式初始化国家:

// With default value


@State var fullText: String = "XXX"


// Not optional value and without default value


@State var fullText: String


init(x: String) {
fullText = x
}


// Optional value and without default value


@State var fullText: String


init(x: String) {
_fullText = State(initialValue: x)
}