Search code examples
swiftxcodeswiftuionchangepicker

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


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)
        }
    })

Solution

  • 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 !!
    }