如何在现有 UIKit 类(如 UIColor)的扩展中添加初始化程序?

Posted

技术标签:

【中文标题】如何在现有 UIKit 类(如 UIColor)的扩展中添加初始化程序?【英文标题】:How to add initializers in extensions to existing UIKit classes such as UIColor? 【发布时间】:2015-03-09 04:19:06 【问题描述】:

Swift 文档说可以在扩展中添加初始化器,文档中的示例是关于向 struct 添加初始化器。 Xcode 在我的便捷初始化程序中无法识别UIColor 的指定初始化程序:

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()

  

有什么解决办法吗?

【问题讨论】:

【参考方案1】:

你不能这样做,你必须选择不同的参数名称来创建你自己的初始化器/你也可以使 then generic 接受任何 BinaryInteger 或 BinaryFloatingPoint 类型:

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

【讨论】:

为什么不用a: 而不是opacity:? :p【参考方案2】:

好吧,如果你真的,真的,真的想重写一个初始化器,有一种方法。

在您进一步阅读之前:永远不要这样做来改变 UIKit 的行为。为什么?它可能会混淆那些无法弄清楚为什么 UIColor 初始化程序没有做它通常做的事情的人。仅用于修复UIKit 错误,或添加功能等。

我已经使用以下方法修补了几个 ios 错误。

代码

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 // 
        ///////////////////////////         

    


说明

这是使用从 Swift 调用的 Objective-C 的动态特性在运行时交换方法定义指针。如果您不知道这意味着什么或它的含义,那么在使用此代码之前阅读该主题可能是个好主意。

【讨论】:

也称为“Swizzeling” 唉,这在未来的某个时候将不再可能。最新版本的 Xcode 提供了一个警告,指出这已被弃用,并且在未来的 Swift 版本中将被禁止。 @KaneCheshire 请在此处查看在未来的 Swift 版本中执行此操作的方法:***.com/a/42824542/555619 是的,我已经发现,这些都没有解决在未来版本的 Swift 中,一旦使用 initialize() 时,您需要手动调用某些东西来启动方法切换过程的问题是不允许的。 【参考方案3】:

更改参数类型也将起作用。

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)
    

用法

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

我通常会忘记将组件值除以 255 的冗余工作。所以我做了这个方法来方便我。

【讨论】:

以上是关于如何在现有 UIKit 类(如 UIColor)的扩展中添加初始化程序?的主要内容,如果未能解决你的问题,请参考以下文章

iOS中的字符串扫描类NSScanner

UIColor各种颜色转换

代码写界面的工厂类

iOS开发UIColor,CGColor,CIColor三者的区别和联系

UIKit:如何将多个 CALayer 混合到一个视图层中?

[iOS]把16进制(#871f78)颜色转换UIColor