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

后端 未结 3 1870
花落未央
花落未央 2021-02-06 23:39

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

相关标签:
3条回答
  • 2021-02-07 00:01

    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.

    0 讨论(0)
  • 2021-02-07 00:10

    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
    
    0 讨论(0)
  • 2021-02-07 00:11

    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.

    0 讨论(0)
提交回复
热议问题