swiftuitabbarcolor-picker

change UITabBar.appearance().tintColor at runtime


I want to allow the user to change the whole App-Color. But whenever I select a color it is only changed after the App will be restarted.

Is there any option to change the color at runtime?

I've set up a Button with a function like this to create a ColorPicker

@IBAction func colorChange(_ sender: UIButton) {
        // Initializing Color Picker
        let picker = UIColorPickerViewController()

        // Setting the Initial Color of the Picker
        picker.selectedColor = UIColor(named: "MyGreen")!

        // Setting Delegate
        picker.delegate = self

        // Presenting the Color Picker
        self.present(picker, animated: true, completion: nil)
    }

And when the user picked a color, I make the changes in this function

func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
        defaults.set(viewController.selectedColor, forKey: "myColor")
        UITabBar.appearance().tintColor = viewController.selectedColor
    }

To change the color at startup I've implemented this in AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        var myColor = defaults.color(forKey: "myColor")
        if  myColor == nil {
            myColor = UIColor(named: "MyGreen")
        }
        UITabBar.appearance().tintColor = myColor
        
        return true
    }

Solution

  • Someone gave me an Article to read how it can be done. https://zamzam.io/protocol-oriented-themes-for-ios-apps/

    I've ended up doing it like this:

     func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
            defaults.set(viewController.selectedColor, forKey: "myColor")
            apply(for: UIApplication.shared, color: viewController.selectedColor)
        }
    

    by adding this function

    func apply(for application: UIApplication, color: UIColor) {
            UITabBar.appearance().tintColor = color
            application.windows.reload()
        }
    

    and these extensions

    public extension UIWindow {
    
        /// Unload all views and add back.
        /// Useful for applying `UIAppearance` changes to existing views.
        func reload() {
            subviews.forEach { view in
                view.removeFromSuperview()
                addSubview(view)
            }
        }
    }
    
    public extension Array where Element == UIWindow {
        
        /// Unload all views for each `UIWindow` and add back.
        /// Useful for applying `UIAppearance` changes to existing views.
        func reload() {
            forEach { $0.reload() }
        }
    }
    

    If it's good or not I don't know - but it worked for me.