web-dev-qa-db-fra.com

Pouvez-vous ajouter des propriétés IBDesignable à UIView à l'aide de catégories/extensions?

Pour ceux qui ne savent pas de quoi je parle, Xcode 6.0 a ajouté de nouvelles fonctionnalités, IBDesignable et IBInspectable.

Lorsque vous marquez vos vues personnalisées avec des propriétés IBInspectable, celles-ci s'affichent dans l'inspecteur des attributs de IB.

De même, lorsque vous balisez une sous-classe UIView personnalisée avec IBDesignable, Xcode compile vos vues et appelle le code pour restituer vos objets de vue directement dans la fenêtre Xcode afin que vous puissiez voir à quoi ils ressemblent.

La technique permettant d'ajouter des attributs IBDesignable et IBInspectable à des vues personnalisées est pratiquement identique dans Swift et Objective-C. Les propriétés IBInspectable apparaissent dans l'inspecteur des attributs d'Interface Builder, quelle que soit la langue utilisée pour les définir.

J'ai créé une catégorie UIView dans Objective-C et une extension de UIView dans Swift qui promeuvent les propriétés borderWidth, cornerRadius, borderColor et layerBackgroundColor du calque sous-jacent de la vue en tant que propriétés de la vue. Si vous modifiez la propriété, l'extension/la catégorie saisit la conversion comme requis et transmet la modification au calque.

La partie IBInspectable fonctionne très bien. Je vois et peux définir les nouvelles propriétés dans l'inspecteur d'attributs IB.

J'aurais pu jurer que la semaine dernière, l'attribut IBDesignable de ma catégorie/extension de vue fonctionnait également, et je pouvais voir mon rendu de catégorie UIView personnalisé dans IB avec ses attributs de couche modifiés. Cette semaine ça ne marche pas.

Étais-je en train d'halluciner?

Les catégories/extensions de classes système existantes peuvent-elles dessiner leur interface utilisateur personnalisée dans Interface Builder lorsqu'elles sont configurées avec IBDesignable?

16
Duncan C

Depuis que j'ai posté cette question, j'ai appris que @IBDesignable ne fonctionne pas pour les extensions de classe. Vous pouvez ajouter la balise, mais cela n'a aucun effet. 

22
Duncan C

@IBDesignable fonctionne avec l'extension UIView uniquement dans une classe personnalisée.Par exemple. UILabel est une sous-classe par défaut d'UIView. Cela ne fonctionnera pas là-bas, mais si vous créez une classe personnalisée appelée MyUILabel sous-classant UILabel. attribuez la classe MyUILabel à l’étiquette sur laquelle vous travaillez. Ensuite, le rayon de votre coin dans l’extension UIView fonctionnera avec ce MyUILabel. (Je suppose que la première semaine, cela fonctionne pour vous parce que vous avez affaire à une classe personnalisée.)

1
Lawrence Cheuk

Ce travail a bien fonctionné pour mon cas d'utilisation en ayant un @IBDesignable UIView que j'ai défini comme vue de dessus dans mon contrôleur de vue. Mon cas d'utilisation particulier est de rendre le style ClassyKit visible dans Interface Builder sur les vues UIKit par défaut sans avoir à sous-classer juste pour cela et cela fonctionne très bien.

Voici un exemple de la façon dont vous pourriez le configurer:

// in Interface Builder set the class of your top view controller view to this
@IBDesignable class DesignableView: UIView {
}

extension UIView {
    open override func prepareForInterfaceBuilder() {
        subviews.forEach {
            $0.prepareForInterfaceBuilder()
        }
    }
}

extension UILabel {
// just an example of doing something
    open override func prepareForInterfaceBuilder() {
        layer.cornerRadius = 8
        layer.masksToBounds = true
        backgroundColor = .red
        textColor = .green
    }
}
0
Richard Burgess

J'ai pu le faire fonctionner avec le code ci-dessous, mais l'effet secondaire est que, parfois, l'agent d'IB dans le scénario de tableau se bloque, car il doit actualiser trop d'éléments d'interface utilisateur. Le redémarrage de Xcode corrige temporairement le problème jusqu'au prochain crash. Peut-être que c'est le problème auquel OP est confronté

@IBDesignable
extension UIView
{

    @IBInspectable
    public var cornerRadius: CGFloat
    {
        set (radius) {
            self.layer.cornerRadius = radius
            self.layer.masksToBounds = radius > 0
        }

        get {
            return self.layer.cornerRadius
        }
    }

    @IBInspectable
    public var borderWidth: CGFloat
    {
        set (borderWidth) {
            self.layer.borderWidth = borderWidth
        }

        get {
            return self.layer.borderWidth
        }
    }

    @IBInspectable
    public var borderColor:UIColor?
    {
        set (color) {
            self.layer.borderColor = color?.cgColor
        }

        get {
            if let color = self.layer.borderColor
            {
                return UIColor(cgColor: color)
            } else {
                return nil
            }
        }
    }
}

C’est pourquoi j’essaie d’ajouter la clause where pour réduire les sous-classes devant étendre cette fonctionnalité: Extension générique d’IBDesginables UIView

0