簡體   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