skip to Main Content

I have a function that accepts a UIColor.

func getColor(_ background: UIColor) -> UIColor {
    switch background {
    case .white, .systemBrown:
        return .black
    case .darkGray:
        return .lightGray
    case .black:
        return .white
    default:
        return .label
    }
}

and I have UIButton with background color .systemBrown

let brownButton: UIButton = {
    let btn = UIButton()
    btn.translatesAutoresizingMaskIntoConstraints = false
    btn.backgroundColor = .systemBrown
    btn.layer.borderWidth = 1.0
    btn.layer.borderColor = UIColor.brown.cgColor
    return btn
}()

When i call the function like this:

getColor(brownButton.backgroundColor!)

It returns .label (default case). But when i use:

getColor(.systemBrown)

I get the expected result

2

Answers


  1. It might be that the system is doing something to the background color like applying a tint to it.

    I would suggest logging the RGB and alpha values of UIColor.systemBrown and of your brownButton’s backgroundColor. You might find that they are slightly different. In that case you might need to write a "color close to another color" comparison. (Say the R, G, and B components are within 0.02 of each other? Or within 5 for integer values from 0-255. I just pulled those ranges out of thin air, but they are probably a good starting point.)

    Something like this:

    extension UIColor {
        func isSimilarToColor(_ otherColor: UIColor) -> Bool {
            var myRed: CGFloat = 0
            var myGreen: CGFloat = 0
            var myBlue: CGFloat = 0
            var myAlpha: CGFloat = 0
    
            var otherRed: CGFloat = 0
            var otherGreen: CGFloat = 0
            var otherBlue: CGFloat = 0
            var otherAlpha: CGFloat = 0
    
            self.getRed(&myRed, green: &myGreen, blue: &myBlue, alpha: &myAlpha)
            otherColor.getRed(&otherRed, green: &otherGreen, blue: &otherBlue, alpha: &otherAlpha)
            let slop: CGFloat = 0.02
            return
                myAlpha == 1.0 && otherAlpha == 1.0 &&
                abs(myRed-otherRed) < slop &&
                abs(myGreen-otherGreen) < slop  &&
                abs(myBlue-otherBlue) < slop
        }
    }
    
    Login or Signup to reply.
  2. The "system" colors are dynamic based on the trait collection of what they are applied to.

    Printing the various colors gives you an idea of the differences:

    let color = view.backgroundColor!
    print("Background: (color)")
    print("SystemBrown: (UIColor.systemBrown)")
    

    The output is:

    Background: <UIDynamicModifiedColor: 0x60000394d6e0; contrast = normal, baseColor = <UIDynamicCatalogSystemColor: 0x600002277000; name = systemBrownColor>>
    SystemBrown: <UIDynamicCatalogSystemColor: 0x600002277000; name = systemBrownColor>

    This indicates that applying the systemBrown color to the view changed the color’s properties a bit. This means it is no longer equal to a "plain" systemBrown color any more.

    One solution is to resolve all relevant colors to the appropriate trait collection.

    Update getColors by resolving any dynamic colors used in case statements:

    func getColor(_ background: UIColor, traits: UITraitCollection) -> UIColor {
        switch background {
        case .white, .systemBrown.resolvedColor(with: traits):
            return .black
        case .darkGray:
            return .lightGray
        case .black:
            return .white
        default:
            return .label
        }
    }
    

    Then update your call to getColors:

    getColor(color.resolvedColor(with: view.traitCollection), traits: view.traitCollection)
    

    You need to resolve the passed background color as well as pass the view’s traitCollection to getColors.

    If you always work on a view’s backgroundColor you could change getColors to take just a UIView parameter and resolve the colors inside that function as needed.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search