The Swift documentation says that adding initializers in an extension is possible, and the example in the document is about adding an initializer to a struct. Xcode doe
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.
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,0
let green2 = UIColor(r: 0, g: 1.0, b: 0, a: 1.0) // r 0,0 g 1,0 b 0,0 a 1,0
let red1 = UIColor(r: 255, g: 0, b: 0) // r 1,0 g 0,0 b 0,0 a 1,0
let 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.
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 //
///////////////////////////
}
}
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.