web-dev-qa-db-fra.com

Comment ajouter des initialiseurs dans des extensions de classes UIKit existantes telles que UIColor?

La documentation de Swift indique qu'il est possible d'ajouter des initialiseurs dans une extension, et l'exemple du document concerne l'ajout d'un initialiseur à un struct. Xcode ne reconnaît pas l'initialiseur désigné par UIColor dans mon initializer de commodité: 

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

  }
}

Des solutions?

20
mrahmiao

Vous ne pouvez pas le faire comme ceci, vous devez choisir différents noms de paramètres pour créer vos propres initialiseurs comme suit:

extension UIColor {
    convenience init(r: UInt8, g: UInt8 , b: UInt8 , a: UInt8) {
        self.init(red: CGFloat(r)/255, green: CGFloat(g)/255, blue: CGFloat(b)/255, alpha: CGFloat(a)/255)
    }
    convenience init(r: Double, g: Double , b: Double , alpha: Double) {
        self.init(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: CGFloat(alpha))
    }
}

let myGreenColor1 = UIColor(r: 0, g: 255, b: 0, a: 255)
let myGreenColor2 = UIColor(r: 0, g: 1, b: 0, alpha: 1)
29
Leo Dabus

Eh bien, si vous voulez vraiment, vraiment, vraiment remplacer un initialiseur, il existe un moyen. 

Avant de poursuivre la lecture : ne faites jamais cela pour changer le comportementUIKit. Pourquoi? Cela pourrait semer la confusion chez quelqu'un qui ne comprend pas pourquoi un initialiseur UIColor ne fait pas ce qu'il fait normalement. Ne le faites que pour corriger un bogue UIKit, ou pour ajouter des fonctionnalités, etc. 

J'ai utilisé ce qui suit pour patcher plusieurs bogues iOS. 

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

    }

}

Explication

Ceci utilise la nature dynamique d'Objective-C, appelée depuis Swift, pour permuter les pointeurs de définition de méthodes au moment de l'exécution. Si vous ne savez pas ce que cela signifie ou ce que cela implique, c'est probablement une bonne idée de lire sur le sujet avant d'utiliser ce code.

2
Jordan Smith

Changer les types de paramètres fonctionnera également. 

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

Utilisation :

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

J'oublierais généralement le travail redondant consistant à diviser les valeurs des composants par 255. J'ai donc conçu cette méthode pour me faciliter la tâche.