为什么我的 SwiftUI 视图不会在更新 @State var 时更新?

时间:2021-07-14 06:55:55

标签: swift swiftui

我遇到了一个奇怪的问题,@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 中正确分配了值;但是,这种分配不会导致视图更新,并且“name”的值不会输入到 TextField 中。

有趣的是,我不知道这是否是一件好事,如果我将 onAppear 块的内容包装在 DispatchQueue.main.async 中,一切正常!

.onAppear {
    DispatchQueue.main.async {
        name = theme.name
        ...
    }
}

有没有人知道如何在 onAppear 中强制刷新视图?或者,为什么对“name”的赋值不会强制更新?

谢谢。

2 个答案:

答案 0 :(得分:0)

这本身并不是答案,但我继续使用以下代码创建了一个新的 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)
        }
    }
}

enter image description here

答案 1 :(得分:0)

我似乎用 init() 解决了我的问题。我创建了 init(theme: Binding<GameTheme>),然后在 init 中通过 _theme = theme 分配了主题,然后通过 _name = State(initialValue: theme.name.wrappedValue) 分配了名称。