SwiftUI @State var 初始化问题
SwiftUI @State var initialization issue
我想通过 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
我该如何解决这个问题?非常感谢您!
我会尝试在 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]!)
}
Bogdan Farca 的回答对于这种情况是正确的,但我们不能说这是所问问题的解决方案,因为我发现所问问题中的文本字段存在问题。我们仍然可以将 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:)
来初始化 View
的 init
中的状态。事实上,State
应该只内联初始化,像这样:
@State private var fullText: String = "The value"
如果这不可行,请使用 @Binding
、@ObservedObject
、@Binding
和 @State
之间的组合,甚至是自定义 DynamicProperty
在您的具体情况下,@Bindable
+ @State
+ onAppear
+ onChange
应该可以解决问题。
关于此的更多信息以及 DynamicProperty
的工作原理,here。
您也可以创建视图模型并启动它:
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 实例化方法。
我想通过 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
我该如何解决这个问题?非常感谢您!
我会尝试在 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]!)
}
Bogdan Farca 的回答对于这种情况是正确的,但我们不能说这是所问问题的解决方案,因为我发现所问问题中的文本字段存在问题。我们仍然可以将 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:)
来初始化 View
的 init
中的状态。事实上,State
应该只内联初始化,像这样:
@State private var fullText: String = "The value"
如果这不可行,请使用 @Binding
、@ObservedObject
、@Binding
和 @State
之间的组合,甚至是自定义 DynamicProperty
在您的具体情况下,@Bindable
+ @State
+ onAppear
+ onChange
应该可以解决问题。
关于此的更多信息以及 DynamicProperty
的工作原理,here。
您也可以创建视图模型并启动它:
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 实例化方法。