Picker in SwiftUI 2 .onChange() does not change UINavigationBar.appearance()

265 Views Asked by At

So this is a weird one...before SwiftUI 2 launched, I set the UINavigationBar.appearance() in the init() of the view as follows:

init(selectedStyle: Binding<Int>) {
        _selectedStyle = selectedStyle
        if self.selectedStyle == 1 {
            UINavigationBar.appearance().backgroundColor = UIColor.init(displayP3Red: 7/255, green: 7/255, blue: 7/255, alpha: 1)
            UISegmentedControl.appearance().backgroundColor = .black
            UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
            UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: UIControl.State.selected)
            UISegmentedControl.appearance().selectedSegmentTintColor = .white
            UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], for: UIControl.State.normal)
        } else {
            UINavigationBar.appearance().backgroundColor = .white
            UISegmentedControl.appearance().backgroundColor = .white
            UISegmentedControl.appearance().selectedSegmentTintColor = .white
            UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
            UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: UIControl.State.selected)
            UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: UIControl.State.normal)
        }
    }

In SwiftUI 1, the view would initialize again so the new look for the navbar would update correctly. because the init() function would run again. I tried to put the same code inside of an onChange() that's attached to the Picker but for some reason it doesn't work:

Picker(selection: $selectedStyle, label: Text("")) {
    ForEach(0 ..< 3) {
        Text([$0])
    }
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: selectedStyle, perform: { change in
    if self.selectedStyle == 1 {
            UINavigationBar.appearance().backgroundColor = UIColor.init(displayP3Red: 7/255, green: 7/255, blue: 7/255, alpha: 1)
            UISegmentedControl.appearance().backgroundColor = .black
            UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
            UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: UIControl.State.selected)
            UISegmentedControl.appearance().selectedSegmentTintColor = .white
            UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.white], for: UIControl.State.normal)
        } else {
            UINavigationBar.appearance().backgroundColor = .white
            UISegmentedControl.appearance().backgroundColor = .white
            UISegmentedControl.appearance().selectedSegmentTintColor = .white
            UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
            UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: UIControl.State.selected)
            UISegmentedControl.appearance().setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: UIControl.State.normal)
        }
    })
1

There are 1 best solutions below

2
Asperi On BEST ANSWER

The appearance have effect on UI elements creates after corresponding appearance is set. So you need to recreate all dependent UI.

The possible approach can be as follows - assuming you have NavigationView in root of ContentView, you can recreate it (and so all subviews), by

var body: some View {
   NavigationView {

     // .. content here

   }.id(selectedStyle)       // << here !!
}