为啥我的 SwiftUI 视图不会在更新 @State var 时更新?
Posted
技术标签:
【中文标题】为啥我的 SwiftUI 视图不会在更新 @State var 时更新?【英文标题】:Why does my SwiftUI View not update on updating of an @State var?为什么我的 SwiftUI 视图不会在更新 @State var 时更新? 【发布时间】:2021-09-23 03:45:17 【问题描述】:我遇到了一个奇怪的问题,即 @State var 没有更新 ios SwiftUI 视图。 我有一个带有游戏主题列表的 NavigationView 的小型游戏主题的编辑屏幕。当处于编辑模式并选择其中一个主题时,我会打开一个编辑器视图,将主题作为绑定传递给编辑器视图结构。 在我的编辑器视图中,我有部分允许用户编辑主题的属性。我不想在我的编辑字段中使用对各种主题属性的绑定,因为我不希望更改立即生效。相反,我为这些属性中的每一个创建了@State 变量,然后在编辑字段中使用这些属性的绑定。这样,我让用户可以选择取消而不使更改生效,或者选择“完成”以通过绑定将更改分配回主题。 为了初始化 @State vars,我有一个 onAppear 块,它从各自的主题属性中分配 @State vars 值。 我遇到的问题是,当执行 onAppear 块并分配变量时,相关的编辑字段没有更新! 这是我的代码的精简版:
struct EditorView: View
/// The current presentation mode of the view.
@Environment(\.presentationMode) var presentationMode
@Binding var theme: GameTheme
@State private var name = ""
...
var body: some View
NavigationView
Form
nameSection
...
.navigationTitle("Edit \(theme.name)")
.toolbar
ToolbarItem(placement: .cancellationAction)
Button("Cancel", action: cancel)
ToolbarItem(placement: .confirmationAction)
Button("Done", action: saveTheme)
.disabled(!canSaveTheme)
.onAppear
name = theme.name
...
.frame(minWidth: Constants.minViewSize.width, minHeight: Constants.minViewSize.height)
var nameSection: some View
Section(header: Text("Name"))
TextField(LocalizedStringKey("Name"), text: $name)
...
所以视图会在出现时显示,@State var 名称确实从 theme.name 中正确分配了值;但是,此分配不会导致视图更新,并且“名称”的值不会输入到 TextField 中。
有趣的是,我不知道这是不是一件好事,如果我将 onAppear 块的内容包装在 DispatchQueue.main.async 中,一切正常!
即
.onAppear
DispatchQueue.main.async
name = theme.name
...
有没有人知道如何在 onAppear 中强制刷新视图?或者,为什么对“name”的赋值不强制更新?
谢谢。
【问题讨论】:
您应该将绑定变量传递给 init 而不是 onAppear。然后你可以使用绑定变量来设置@State变量。 因为您要更改class GameTheme
中的变量。 @State
观察整个对象。这意味着它只会在它是 struct
或类似 String
的情况下发生变化。如果要观察 class
的变量,则必须使其符合 ObservableObject
并用 @Published
包装变量,然后使用 @StateObject
或 @ObservedObjec
我似乎已经用 init() 解决了我的问题。我创建了init(theme: Binding<GameTheme>)
,然后在init中通过_theme = theme
分配了主题,然后通过_name = State(initialValue: theme.name.wrappedValue)
分配了名称。
【参考方案1】:
这本身不是答案,但我继续使用以下代码创建了一个新的 iOS 项目(基于您的帖子,但我对其进行了一些清理,并自己想出了缺少的 GameTheme
对象)。
它或多或少相同,并表明您发布的结构确实重新渲染。
我想知道我们在您的帖子中看不到的代码是否还有其他可能导致此问题的原因。
您是否可能在其他任何地方设置 name
状态变量以覆盖加载时的值?
import SwiftUI
@main
struct TestIOSApp: App
@State var gameTheme: GameTheme = GameTheme(name: "A game theme")
var body: some Scene
WindowGroup
ContentView(theme: $gameTheme)
struct GameTheme
var name:String;
struct ContentView: View
@Binding var theme:GameTheme;
/// The current presentation mode of the view.
@Environment(\.presentationMode) var presentationMode
@State private var name = "DEFAULT SHOULD NOT BE DISPLAYED"
var body: some View
NavigationView
Form
nameSection
.navigationTitle("Edit \(theme.name)")
.onAppear
name = theme.name
.toolbar
ToolbarItem(placement: .cancellationAction)
Button("Cancel", action: )
ToolbarItem(placement: .confirmationAction)
Button("Done", action: )
.frame(maxWidth:.infinity, maxHeight: .infinity)
var nameSection: some View
Section(header: Text("Name"))
TextField(LocalizedStringKey("Name"), text: $name)
【讨论】:
【参考方案2】:我似乎已经用 init() 解决了我的问题。我创建了init(theme: Binding<GameTheme>)
,然后在init中通过_theme = theme
分配了主题,然后通过_name = State(initialValue: theme.name.wrappedValue)
分配了名称。
【讨论】:
以上是关于为啥我的 SwiftUI 视图不会在更新 @State var 时更新?的主要内容,如果未能解决你的问题,请参考以下文章
为啥核心数据不会在 swiftui 类中加载,但会在结构视图中加载