简体   繁体   中英

Value of Selected Option From a SwiftUI Picker does not Update the View

I have the following in a SwiftUI app. Basically I have some settings (Settings class) that I would like to use throughout the app. I have a Settings view that shows a picker to select the value of one of the settings. And other views of the app would only use the current set value of the settings. The following setup works in the sense that in ContentView I see the correct value of firstLevel setting. But the problem is that in SettingsView, I think since selectedFirstLevel is not a @State, its correct value is not shown on the picker I navigate to select either even or odd (oddly, the first time it's correct). This selection is carried correctly to ContentView, but it's not shown correctly on SettingsView. How can I fix this issue?

Settings.swift

import Foundation

class Settings: ObservableObject {
    static let shared: Settings = Settings()
    @Published var firstLevel: FirstLevel = .even
}

enum FirstLevel: String, CaseIterable, Identifiable {
    case even
    case odd
    var id: String { self.rawValue }
}

ContentView.swift

import SwiftUI

struct ContentView: View {
    @State private var showSettings: Bool = false
    @ObservedObject var settings = Settings.shared
    var body: some View {
        VStack {
            SettingsButton(showSettings: $showSettings, settings: settings)
            Text(settings.firstLevel.id)
                .padding()
        }
        
    }
}

struct SettingsButton: View {
    @Binding var showSettings: Bool
    var settings: Settings
    var firstLevel: Binding<FirstLevel> {
        return Binding<FirstLevel>(
            get: {
                return self.settings.firstLevel
        }) { newFirstLevel in
            self.settings.firstLevel = newFirstLevel
        }
    }
    var body: some View {
        Button(action: { self.showSettings = true }) {
            Image(systemName: "gear").imageScale(.large)
        }
        .sheet(isPresented: $showSettings) {
            SettingsView(selectedFirstLevel: self.firstLevel)
        }
    }
}

SettingsView.swift

import SwiftUI

struct SettingsView: View {

    @Binding var selectedFirstLevel: FirstLevel
    var body: some View {
        NavigationView {
            Form {
                Picker("First Level", selection: $selectedFirstLevel) {
                    ForEach(FirstLevel.allCases) { level in
                        Text(level.rawValue).tag(level)
                    }
                }
            }
            .navigationBarTitle("Settings", displayMode: .inline)
        }
    }
}

It looks overcomplicated, moreover Binding is unreliable as communication between different view hierarchies (which is sheet in your case).

Here is simplified and worked variant. Tested with Xcode 12 / iOS 14.

struct ContentView: View {
    @ObservedObject var settings = FLevelSettings.shared
    var body: some View {
        VStack {
            SettingsButton(settings: settings)
            Text(settings.firstLevel.id)
                .padding()
        }

    }
}

struct SettingsButton: View {
    @State private var showSettings: Bool = false
    var settings: FLevelSettings
    var body: some View {
        Button(action: { self.showSettings = true }) {
            Image(systemName: "gear").imageScale(.large)
        }
        .sheet(isPresented: $showSettings) {
            FLevelSettingsView(settings: self.settings)
        }
    }
}
struct FLevelSettingsView: View {

    @ObservedObject var settings: FLevelSettings
    var body: some View {
        NavigationView {
            Form {
                Picker("First Level", selection: $settings.firstLevel) {
                    ForEach(FirstLevel.allCases) { level in
                        Text(level.rawValue).tag(level)
                    }
                }
            }
            .navigationBarTitle("Settings", displayMode: .inline)
        }
    }
}

Note: it can be even more simplified, if you want, due to presence of FLevelSettings.shared , so you can use it inside FLevelSettingsView directly. Just in case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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