Dark & Light Theme Manager on iOS 13

iOS 13 giving developers another challenge to implement most requested feature on iOS, a dark theme. To implement the theme is not difficult, but how do we make it clear and maintainable is quite a challenge.  

My approach is to make a protocol and each of the theme will implement that protocol.

First, we need to create a protocol with number of required variables.

public protocol Themed {
    var background: UIColor {get}
    var main: UIColor {get}
    var mainText: UIColor {get}
}

Create a theme that implement that above protocol. We will create DarkTheme followed with LightTheme.

struct DarkTheme: Themed {
    var main: UIColor {
        return UIColor(hex: 0xFF0000)
    }
    
    var mainText: UIColor {
        return UIColor(hex: 0xEFEFEF)
    }
    
    var background: UIColor {
        return UIColor(hex: 0x353535)
    }
}
struct LightTheme: Themed {
    var main: UIColor {
        return UIColor(hex: 0x00FF00)
    }
    
    var mainText: UIColor {
        return UIColor(hex: 0x353535)
    }
    
    var background: UIColor {
        return UIColor(hex: 0xFFFFFF)
    }
}

And the most important thing, create a struct that will be used to get a color.

struct Theme {
    static var active: Themed {
        return (UIApplication.isDark()) ? DarkTheme() : LightTheme()
    }
}

Then you could use the color with this line:

view.backgroundColor = Theme.active.main

With the approach above, if you wanna create a new theme, you may create another struct. And you may create another property in the protocol as needed.

If you wonder how I got that UIApplication.isDark() and UIColor(hex: 0xFFFFFF, add the extension below.

extension UIApplication {
    
    static func isDark() -> Bool {
        if #available(iOS 13.0, *) {
            return (UIScreen.main.traitCollection.userInterfaceStyle == .dark)
        }
        
        return false
    }
}

extension UIColor {
    public var hexValue: Int {
        var rF: CGFloat = 0
        var gF: CGFloat = 0
        var bF: CGFloat = 0
        getRed(&rF, green: &gF, blue: &bF, alpha: nil)
        
        let r = Int(rF*255)
        let g = Int(gF*255)
        let b = Int(bF*255)
        
        return r << 16 + g << 8 + b
    }
    
    public convenience init(hex value: Int, alpha: CGFloat = 1) {
        let r = (value & 0xFF0000) >> 16
        let g = (value & 0x00FF00) >> 8
        let b = value & 0x0000FF
        
        self.init(red: CGFloat(r)/255, green: CGFloat(g)/255, blue: CGFloat(b)/255, alpha: alpha)
    }
}
Show Comments