How to add initializers in extensions to existing UIKit classes such as UIColor? How to add initializers in extensions to existing UIKit classes such as UIColor? swift swift

How to add initializers in extensions to existing UIKit classes such as UIColor?


You can't do it like this, you have to chose different parameter names to create your own initializers/ You can also make then generic to accept any BinaryInteger or BinaryFloatingPoint types:

extension UIColor {    convenience init<T: BinaryInteger>(r: T, g: T, b: T, a: T = 255) {        self.init(red: .init(r)/255, green: .init(g)/255, blue: .init(b)/255, alpha: .init(a)/255)    }    convenience init<T: BinaryFloatingPoint>(r: T, g: T, b: T, a: T = 1.0) {        self.init(red: .init(r), green: .init(g), blue: .init(b), alpha: .init(a))    }}

let green1 = UIColor(r: 0, g: 255, b: 0, a: 255)  // r 0,0 g 1,0 b 0,0 a 1,0let green2 = UIColor(r: 0, g: 1.0, b: 0, a: 1.0)  // r 0,0 g 1,0 b 0,0 a 1,0let red1 = UIColor(r: 255, g: 0, b: 0)  // r 1,0 g 0,0 b 0,0 a 1,0let red2 = UIColor(r: 1.0, g: 0, b: 0)  // r 1,0 g 0,0 b 0,0 a 1,0


Well, if you really, really, really want to override an initialiser, there is a way.

Before you read further: never do this to change UIKit behaviour. Why? It could confuse the heck out of someone that can't figure out why a UIColor initialiser isn't doing what it normally does. Only do it to fix a UIKit bug, or add functionality, etc.

I have used the following to patch several iOS bugs.

Code

extension UIColor {    private static var needsToOverrideInit = true    override open class func initialize() {        // Only run once - otherwise subclasses will call this too. Not obvious.        if needsToOverrideInit {            let defaultInit = class_getInstanceMethod(UIColor.self, #selector(UIColor.init(red:green:blue:alpha:)))            let ourInit = class_getInstanceMethod(UIViewController.self, #selector(UIColor.init(_red:_green:_blue:_alpha:)))            method_exchangeImplementations(defaultInit, ourInit)            needsToOverrideInit = false        }    }    convenience init(_red: CGFloat, _green: CGFloat, _blue: CGFloat, _alpha: CGFloat) {        // This is trippy. We swapped implementations... won't recurse.        self.init(red: _red, green: _green, blue: _blue, alpha: _alpha)        ///////////////////////////         // Add custom logic here //         ///////////////////////////             }}

Explanation

This is using the dynamic nature of Objective-C, called from Swift, to swap method definition pointers at runtime. If you don't know what this means, or implications of it, it is probably a good idea to read up on the topic before you use this code.


Changing the parameter types will also work.

extension UIColor {    convenience init(red: Int, green: Int, blue: Int, alpha: CGFloat) {        let normalizedRed = CGFloat(red) / 255        let normalizedGreen = CGFloat(green) / 255        let normalizedBlue = CGFloat(blue) / 255        self.init(red: normalizedRed, green: normalizedGreen, blue: normalizedBlue, alpha: alpha)    }}

Usage:

let newColor: UIColor = UIColor.init(red: 74, green: 74, blue: 74, alpha: 1)

I would usually forget the redundant work of dividing the component values by 255. So I made this method to facilitate me.