如何在现有 UIKit 类 的扩展中添加初始值设定项,例如 UIColor?
How to add initializers in extensions to existing UIKit classes such as UIColor?
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()
}
}
有什么解决办法吗?
你不能这样做,你必须选择不同的参数名称来创建你自己的初始化程序/你也可以使 then 通用以接受任何 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
好吧,如果你真的、真的、真的想要覆盖一个初始化器,有办法。
在进一步阅读之前:永远不要这样做来改变 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 的动态特性,在运行时交换方法定义指针。如果您不知道这意味着什么或它的含义,在使用此代码之前阅读该主题可能是个好主意。
更改参数类型也可以。
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的冗余工作。所以我做了这个方法来方便我。
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()
}
}
有什么解决办法吗?
你不能这样做,你必须选择不同的参数名称来创建你自己的初始化程序/你也可以使 then 通用以接受任何 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
好吧,如果你真的、真的、真的想要覆盖一个初始化器,有办法。
在进一步阅读之前:永远不要这样做来改变 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 的动态特性,在运行时交换方法定义指针。如果您不知道这意味着什么或它的含义,在使用此代码之前阅读该主题可能是个好主意。
更改参数类型也可以。
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的冗余工作。所以我做了这个方法来方便我。