Home > database >  UITabBarController Appearance Only Allows Me to Set It Once
UITabBarController Appearance Only Allows Me to Set It Once

Time:07-04

Here's the issue:

I have an extension that I've written to allow simple customization of the Tab Bar in a standard UIKit app.

The issue is that it works fine -once.

Subsequent attempts to change the appearance are ignored.

I'd be grateful for any assistance in telling me what I am doing wrong.

Below is the extension. If I call setColorsTo(normal:, selected:, disabled: background:), it works fine, once; but calling it again, does nothing.

Any ideas?

/* ###################################################################################################################################### */
// MARK: - UITabBarController Extension -
/* ###################################################################################################################################### */
/**
 This allows setting the colors for a tab bar.
 */
public extension UITabBarController {
    /* ################################################################## */
    /**
     This allows us to set specific colors for the normal, selected, and background attributes of the tab bar.
     All parameters are optional. If not provided, default values for the current theme are used.
     - parameters:
        - normal: The color to use for an unselected, enabled tab item.
        - selected: The color to use for a selected tab item.
        - disabled: The color to use for a disabled tab item.
        - focused: The color to use for a focused tab item.
        - background: The background color to use for the bar.
     */
    func setColorsTo(normal inNormalColor: UIColor? = nil,
                     selected inSelectedColor: UIColor? = nil,
                     disabled inDisabledColor: UIColor? = nil,
                     focused inFocusedColor: UIColor? = nil,
                     background inBackgroundColor: UIColor? = nil) {

        let appearance = UITabBarAppearance()

        appearance.configureWithOpaqueBackground()
        
        if let backgroundColor = inBackgroundColor {
            tabBar.backgroundColor = backgroundColor
            tabBar.barTintColor = backgroundColor
            appearance.backgroundColor = backgroundColor
        }

        if let normalColor = inNormalColor {
            appearance.stackedLayoutAppearance.normal.iconColor = normalColor
            appearance.inlineLayoutAppearance.normal.iconColor = normalColor
            appearance.compactInlineLayoutAppearance.normal.iconColor = normalColor
            let normalTextAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: normalColor]
            appearance.stackedLayoutAppearance.normal.titleTextAttributes = normalTextAttributes
            appearance.inlineLayoutAppearance.normal.titleTextAttributes = normalTextAttributes
            appearance.compactInlineLayoutAppearance.normal.titleTextAttributes = normalTextAttributes
        }
        
        if let selectedColor = inSelectedColor {
            appearance.stackedLayoutAppearance.selected.iconColor = selectedColor
            appearance.inlineLayoutAppearance.selected.iconColor = selectedColor
            appearance.compactInlineLayoutAppearance.selected.iconColor = selectedColor
            let selectedTextAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: selectedColor]
            appearance.stackedLayoutAppearance.selected.titleTextAttributes = selectedTextAttributes
            appearance.inlineLayoutAppearance.selected.titleTextAttributes = selectedTextAttributes
            appearance.compactInlineLayoutAppearance.selected.titleTextAttributes = selectedTextAttributes
        }
        
        if let disabledColor = inDisabledColor {
            appearance.stackedLayoutAppearance.disabled.iconColor = disabledColor
            appearance.compactInlineLayoutAppearance.disabled.iconColor = disabledColor
            appearance.inlineLayoutAppearance.disabled.iconColor = disabledColor
            let disabledTextAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: disabledColor]
            appearance.stackedLayoutAppearance.disabled.titleTextAttributes = disabledTextAttributes
            appearance.inlineLayoutAppearance.disabled.titleTextAttributes = disabledTextAttributes
            appearance.compactInlineLayoutAppearance.disabled.titleTextAttributes = disabledTextAttributes
        }
        
        if let focusedColor = inFocusedColor {
            appearance.stackedLayoutAppearance.focused.iconColor = focusedColor
            appearance.inlineLayoutAppearance.focused.iconColor = focusedColor
            appearance.compactInlineLayoutAppearance.focused.iconColor = focusedColor
            let focusedTextAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: focusedColor]
            appearance.stackedLayoutAppearance.focused.titleTextAttributes = focusedTextAttributes
            appearance.inlineLayoutAppearance.focused.titleTextAttributes = focusedTextAttributes
            appearance.compactInlineLayoutAppearance.focused.titleTextAttributes = focusedTextAttributes
        }

        tabBar.standardAppearance = appearance

        if #available(iOS 15.0, *) {
            tabBar.scrollEdgeAppearance = appearance
        }
    }
}

CodePudding user response:

Can't reproduce any issue. Your code works fine for me, if I remove the parts of it that are wrong. For example, here's a reduction of your code that just sets the background color of the tab bar, with the wrong lines of code removed, plus the color of the selected tab bar item icon and text — along with view controller code that exercises it:

extension UITabBarController {
    func setColorsTo(selected inSelectedColor: UIColor? = nil,
                     background inBackgroundColor: UIColor? = nil) {
        let appearance = UITabBarAppearance()
        appearance.configureWithOpaqueBackground()
        if let backgroundColor = inBackgroundColor {
            //tabBar.backgroundColor = backgroundColor // wrong
            //tabBar.barTintColor = backgroundColor // wrong
            appearance.backgroundColor = backgroundColor
        }
        if let selectedColor = inSelectedColor {
            appearance.stackedLayoutAppearance.selected.iconColor = selectedColor
            appearance.inlineLayoutAppearance.selected.iconColor = selectedColor
            appearance.compactInlineLayoutAppearance.selected.iconColor = selectedColor
            let selectedTextAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: selectedColor]
            appearance.stackedLayoutAppearance.selected.titleTextAttributes = selectedTextAttributes
            appearance.inlineLayoutAppearance.selected.titleTextAttributes = selectedTextAttributes
            appearance.compactInlineLayoutAppearance.selected.titleTextAttributes = selectedTextAttributes
        }

        tabBar.standardAppearance = appearance
        if #available(iOS 15.0, *) {
            tabBar.scrollEdgeAppearance = appearance
        }
    }
}

func delay(_ delay:Double, closure:@escaping ()->()) {
    let when = DispatchTime.now()   delay
    DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        delay(2) {
            self.tabBarController?.setColorsTo(selected: .yellow, background: .red)
            delay(2) {
                self.tabBarController?.setColorsTo(selected: .purple, background: .green)
            }
        }
    }
}

When I run the app, the tab bar turns red, then green, while the selected tab bar item turns yellow, then purple — proving that it works more than once. Therefore I conclude that the issue, if there is one, is caused by other code that you have not told us about.

CodePudding user response:

OK. I figured it out.

I needed to add a setNeedsLayout() to the tab bar, after setting the appearance:

/* ################################################################## */
/**
 This allows us to set specific colors for the normal, selected, disabled, focused, and background attributes of the tab bar.
 All parameters are optional. If not provided, default values for the current theme are used.
 - parameters:
    - normal: The color to use for an unselected, enabled tab item.
    - selected: The color to use for a selected tab item.
    - disabled: The color to use for a disabled tab item.
    - focused: The color to use for a focused tab item.
    - background: The background color to use for the bar.
 */
func setColorsTo(normal inNormalColor: UIColor? = nil,
                 selected inSelectedColor: UIColor? = nil,
                 disabled inDisabledColor: UIColor? = nil,
                 focused inFocusedColor: UIColor? = nil,
                 background inBackgroundColor: UIColor? = nil) {

    let appearance = UITabBarAppearance()

    appearance.configureWithOpaqueBackground()
    
    if let backgroundColor = inBackgroundColor {
        appearance.backgroundColor = backgroundColor
    }

    if let normalColor = inNormalColor {
        appearance.stackedLayoutAppearance.normal.iconColor = normalColor
        appearance.inlineLayoutAppearance.normal.iconColor = normalColor
        appearance.compactInlineLayoutAppearance.normal.iconColor = normalColor
        let normalTextAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: normalColor]
        appearance.stackedLayoutAppearance.normal.titleTextAttributes = normalTextAttributes
        appearance.inlineLayoutAppearance.normal.titleTextAttributes = normalTextAttributes
        appearance.compactInlineLayoutAppearance.normal.titleTextAttributes = normalTextAttributes
    }
    
    if let selectedColor = inSelectedColor {
        appearance.stackedLayoutAppearance.selected.iconColor = selectedColor
        appearance.inlineLayoutAppearance.selected.iconColor = selectedColor
        appearance.compactInlineLayoutAppearance.selected.iconColor = selectedColor
        let selectedTextAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: selectedColor]
        appearance.stackedLayoutAppearance.selected.titleTextAttributes = selectedTextAttributes
        appearance.inlineLayoutAppearance.selected.titleTextAttributes = selectedTextAttributes
        appearance.compactInlineLayoutAppearance.selected.titleTextAttributes = selectedTextAttributes
    }
    
    if let disabledColor = inDisabledColor {
        appearance.stackedLayoutAppearance.disabled.iconColor = disabledColor
        appearance.compactInlineLayoutAppearance.disabled.iconColor = disabledColor
        appearance.inlineLayoutAppearance.disabled.iconColor = disabledColor
        let disabledTextAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: disabledColor]
        appearance.stackedLayoutAppearance.disabled.titleTextAttributes = disabledTextAttributes
        appearance.inlineLayoutAppearance.disabled.titleTextAttributes = disabledTextAttributes
        appearance.compactInlineLayoutAppearance.disabled.titleTextAttributes = disabledTextAttributes
    }
    
    if let focusedColor = inFocusedColor {
        appearance.stackedLayoutAppearance.focused.iconColor = focusedColor
        appearance.inlineLayoutAppearance.focused.iconColor = focusedColor
        appearance.compactInlineLayoutAppearance.focused.iconColor = focusedColor
        let focusedTextAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.foregroundColor: focusedColor]
        appearance.stackedLayoutAppearance.focused.titleTextAttributes = focusedTextAttributes
        appearance.inlineLayoutAppearance.focused.titleTextAttributes = focusedTextAttributes
        appearance.compactInlineLayoutAppearance.focused.titleTextAttributes = focusedTextAttributes
    }

    tabBar.standardAppearance = appearance

    if #available(iOS 15.0, *) {
        tabBar.scrollEdgeAppearance = appearance
    }
    
    tabBar.setNeedsLayout()
}
  • Related