繁体   English   中英

SwiftUI:在子视图中更改 UserDefaults 时更新父视图

[英]SwiftUI: Update parent view on change of UserDefaults in child view

我正在用最小的配色方案制作游戏。 我将 colors 用作计算的static var存储在枚举中,以便我可以在任何视图中调用它们。 我正在尝试制作辅助配色方案(色盲)。 我的代码如下所示:

enum GameColors {
    static var exampleColor: Color {
        !UserDefaults.standard.bool(forKey: GamePrefs.colorBlindMode) ? Color.green : Color(red: 0 / 255, green: 213 / 255, blue: 255 / 255)
    }
}
enum GamePrefs {
     static let colorBlindMode: String = "colorBlindMode"
}

我的设置菜单在我的主菜单视图中被调用,如下所示:

struct MainMenuView: View {
    @State var settingsClicked: Bool = false

    var body: some View {
        VStack {
            Button {
                settingsClicked.toggle()
            } label: {
                Text("Settings")
                .foregroundColor(GameColors.exampleColor)
            }
            if settingsClicked {
                SettingsView()
            }
        }
    }
}

struct SettingsView: View {
    @AppStorage(GamePrefs.colorBlindMode) var colorBlindMode = false
    var body: some View {
        Toggle(isOn: $colorBlindMode) {
            Text("Color Blind Mode: \(colorBlindMode ? "On" : "Off")")
                .foregroundColor(GameColors.exampleColor)
        }
    }
}

当我切换colorBlindMode时,只有SettingsView的 colors 被更新,除非我与之交互,否则主菜单的颜色不会改变。 如何让两者都更新?

我尝试将@AppStorage属性包装器绑定到没有成功。

例子

您可以尝试这种方法,在MainMenuView中使用@AppStorage.onReceive ,以确保在单击SettingsView时在MainMenuView中更新/接收GameColors.exampleColor

struct MainMenuView: View {
    @AppStorage(GamePrefs.colorBlindMode) var colorBlindMode = false  // <-- here
    @State var settingsClicked: Bool = false
    
    var body: some View {
        VStack {
            Button {
                settingsClicked.toggle()
            } label: {
                Text("Settings").foregroundColor(GameColors.exampleColor)
            }
            if settingsClicked {
                SettingsView() 
            }
        }
        .onReceive(Just(colorBlindMode)) { _ in  // <-- here
        }
    }
}

struct SettingsView: View {
    @AppStorage(GamePrefs.colorBlindMode) var colorBlindMode = false
    
    var body: some View {
        Toggle(isOn: $colorBlindMode) {
            Text("Color Blind Mode: \(colorBlindMode ? "On" : "Off")")
                .foregroundColor(GameColors.exampleColor)  // <-- here for testing
        }
        .toggleStyle(SwitchToggleStyle(tint: GameColors.exampleColor))
    }
}

你的颜色没有改变的原因是你的MainMenuView在你按下切换时没有刷新。 只有您的SettingsView被刷新。 所以你需要一些东西来通知你的MainMenuView有些东西已经改变了。

为此,您可以向MainMenuView添加另一个 AppStorage,并通过创建对此属性的依赖项来强制刷新视图。

struct MainMenuView: View {
    @State var settingsClicked: Bool = false
    // add the same AppStorage as in SettingsView
    @AppStorage(GamePrefs.colorBlindMode) var colorBlindMode = false
    var body: some View {
        VStack {
            Button {
                settingsClicked.toggle()
            } label: {
                let _ = print("text")
                Text("Settings")
                    //this will force the view to update
                    .foregroundColor(colorBlindMode ? GameColors.exampleColor : GameColors.exampleColor)
            }
            if settingsClicked {
                SettingsView()
            }
        }
    }
}

在阅读了有关导致 SwiftUI 重新加载视图的原因以及大量测试不同方法的更多信息之后,我相信解决此问题的最简单方法是引入一个新的@State变量,您必须在父视图的某处使用它:

struct MainMenuView: View {
    @State var settingsClicked: Bool = false
    @State var reloadView: Bool = false        // <-- here

    var body: some View {
        VStack {
            Button {
                settingsClicked.toggle()
            } label: {
                Text("Settings")
                .foregroundColor(GameColors.exampleColor)
            }
            if settingsClicked {
                SettingsView(reloadParentView: $reloadView)
            }
            if reloadView {}                   // <-- here
        }
    }
}

struct SettingsView: View {
    @Binding var reloadParentView: Bool        // <-- here
    @AppStorage(GamePrefs.colorBlindMode) var colorBlindMode = false
    var body: some View {
        Toggle(isOn: $colorBlindMode) {
            Text("Color Blind Mode: \(colorBlindMode ? "On" : "Off")")
                .foregroundColor(GameColors.exampleColor)
        }
        .onChange(of: colorBlindMode) { _ in
            reloadParentView.toggle()        // <-- here
        }
    }
}

将此 state 从父视图传递到子视图允许子视图随意强制重新加载父视图。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM