Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

swift

uicolor

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 doesn't recognize UIColor's designated initializer in my convenience initializer:

extension UIColor {   convenience init(rawValue red: CGFloat, green g: CGFloat, blue b: CGFloat, alpha a: CGFloat) {      // Can not find out the designated initializer here     self.init()    } } 

Any solutions?

like image 657
mrahmiao Avatar asked Jan 11 '15 05:01

mrahmiao


People also ask

Can you add a stored property to a type by using an extension?

As you may know Swift does not allow stored properties into extensions. That's by design: “Extensions may not contain stored properties.”

How do I use Swift 5 Extension?

Creating an extension in Swift Creating extensions is similar to creating named types in Swift. When creating an extension, you add the word extension before the name. extension SomeNamedType { // Extending SomeNamedType, and adding new // functionality to it. }

What are Initializers in Swift?

An initializer is a special type of function that is used to create an object of a class or struct. In Swift, we use the init() method to create an initializer. For example, class Wall { ... // create an initializer init() { // perform initialization ... } }


2 Answers

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 
like image 70
Leo Dabus Avatar answered Sep 30 '22 07:09

Leo Dabus


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.

like image 22
Jordan Smith Avatar answered Sep 30 '22 05:09

Jordan Smith