SwiftUI @State var 初始化问题
Posted
技术标签:
【中文标题】SwiftUI @State var 初始化问题【英文标题】:SwiftUI @State var initialization issue 【发布时间】:2019-06-20 18:07:24 【问题描述】:我想通过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 可能是您想要的答案,而不是接受的答案。
【参考方案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 而是内联。
答案不是解决问题的办法。它不会像梦一样工作,而是噩梦。在这里,如果我们想改变视图中的值,我们需要使用可以在初始化器中初始化的绑定。在 init 中初始化 @State 只会在第一次创建此视图的值时进行。后续视图将不会看到在 init 中作为参数传递的值,而是由 SwiftUI 系统缓存的前一个值。【参考方案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】:
现在在init
方法中设置@State
变量的默认值已经不是问题了。但是你必须去掉你给状态的默认值,它会按需要工作:
,,,
@State var fullText: String // <- No default value here
init(letter: String)
self.fullText = list[letter]!
var body: some View
TextField("", text: $fullText)
【讨论】:
【参考方案4】: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")
【讨论】:
【参考方案5】:上面的答案不正确。永远不要使用State(initialValue:)
或State(wrappedValue:)
来初始化View
的init
中的状态。事实上,State
应该只内联初始化,像这样:
@State private var fullText: String = "The value"
如果这不可行,请使用 @Binding
、@ObservedObject
、@Binding
和 @State
之间的组合,甚至是自定义 DynamicProperty
在您的具体情况下,@Bindable
+ @State
+ onAppear
+ onChange
应该可以解决问题。
更多关于这方面的信息以及DynamicProperty
s 的工作原理,here。
【讨论】:
【参考方案6】:您也可以创建视图模型并启动它:
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 实例化方法。
【讨论】:
【参考方案7】:请参阅下面示例中的.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)")
【讨论】:
以上是关于SwiftUI @State var 初始化问题的主要内容,如果未能解决你的问题,请参考以下文章
如何在 SwiftUI 中使用 UIButton 更改 @State var
从 @Binding var 修改 @State var 不会刷新 SwiftUI 中的视图
无法更新/修改 SwiftUI 视图的 @state var
为啥我的 SwiftUI 视图不会在更新 @State var 时更新?