web-dev-qa-db-fra.com

Bonne pratique pour sous-classer UIView?

Je travaille sur des contrôles d'entrée personnalisés basés sur UIView et j'essaie de déterminer les bonnes pratiques pour configurer la vue. Lorsque vous travaillez avec UIViewController, il est assez simple d'utiliser les méthodes loadView et les méthodes viewWill, viewDid associées, mais lorsque vous sous-classez UIView, les méthosds les plus proches que je possède sont `awakeFromNib, drawRect et layoutSubviews. (Je pense en termes de configuration et de rappel de démontage.) Dans mon cas, je configure mon cadre et mes vues internes dans layoutSubviews, mais je ne vois rien à l'écran.

Quelle est la meilleure façon de s’assurer que ma vue a la hauteur et la largeur correctes? (Ma question s'applique que j'utilise ou non l'autolayout, bien qu'il puisse y avoir deux réponses.) Quelle est la "meilleure pratique" appropriée?

151
Moshe

Apple a défini assez clairement comment sous-classer UIView dans le document.

Consultez la liste ci-dessous, en particulier jeter un oeil à initWithFrame: et layoutSubviews. Le premier est destiné à configurer le cadre de votre UIView tandis que le dernier est destiné à configurer le cadre et la présentation de ses sous-vues.

Rappelez-vous aussi que initWithFrame: est appelé uniquement si vous instanciez votre UIView par programme. Si vous le chargez depuis un fichier nib (ou un storyboard), initWithCoder: sera utilisé. Et en initWithCoder: le cadre n’a pas encore été calculé; vous ne pouvez donc pas modifier le cadre que vous avez configuré dans Interface Builder. Comme suggéré dans cette réponse vous pouvez penser à appeler initWithFrame: de initWithCoder: afin de configurer le cadre.

Enfin, si vous chargez votre UIView à partir d’une plume (ou d’un storyboard), vous avez également la possibilité awakeFromNib d’effectuer des initialisations de cadre et de présentation personnalisées, à partir du moment où awakeFromNib est appelé. il est garanti que chaque vue de la hiérarchie a été désarchivée et initialisée.

Extrait de la doc de NSNibAwaking (maintenant remplacé par la doc de awakeFromNib ):

Les messages vers d'autres objets peuvent être envoyés en toute sécurité depuis awakeFromNib. À ce moment-là, il est garanti que tous les objets sont désarchivés et initialisés (bien que pas nécessairement réveillés, bien sûr).

Il est également intéressant de noter qu'avec l'autolayout, vous ne devez pas définir explicitement le cadre de votre vue. Au lieu de cela, vous êtes censé spécifier un ensemble de contraintes suffisantes pour que le cadre soit automatiquement calculé par le moteur de présentation.

Directement du documentation :

Méthodes pour remplacer

Initialisation

  • initWithFrame: Il est recommandé de mettre en œuvre cette méthode. Vous pouvez également implémenter des méthodes d'initialisation personnalisées en plus ou au lieu de cette méthode.

  • initWithCoder: Implémentez cette méthode si vous chargez votre vue à partir d’un fichier nib d’Interface Builder et que votre vue nécessite une initialisation personnalisée.

  • layerClass Implémentez cette méthode uniquement si vous souhaitez que votre vue utilise un calque Core Animation différent pour son magasin de support. Par exemple, si vous utilisez OpenGL ES pour effectuer votre dessin, vous souhaitez remplacer cette méthode et renvoyer la classe CAEAGLLayer.

Dessin et impression

  • drawRect: Implémentez cette méthode si votre vue dessine un contenu personnalisé. Si votre vue ne fait aucun dessin personnalisé, évitez de remplacer cette méthode.

  • drawRect:forViewPrintFormatter: Mettez en œuvre cette méthode uniquement si vous souhaitez dessiner différemment le contenu de votre vue lors de l’impression.

Contraintes

  • requiresConstraintBasedLayout Implémentez cette méthode de classe si votre classe de vue requiert des contraintes pour fonctionner correctement.

  • updateConstraints Implémentez cette méthode si votre vue doit créer des contraintes personnalisées entre vos sous-vues.

  • alignmentRectForFrame:, frameForAlignmentRect: Implémentez ces méthodes pour redéfinir l'alignement de vos vues sur d'autres vues.

Disposition

  • sizeThatFits: Implémentez cette méthode si vous souhaitez que votre vue ait une taille par défaut différente de celle qu'elle aurait normalement lors des opérations de redimensionnement. Par exemple, vous pouvez utiliser cette méthode pour empêcher votre vue de se réduire au point où les sous-vues ne peuvent pas être affichées correctement.

  • layoutSubviews Implémentez cette méthode si vous avez besoin d'un contrôle plus précis sur la présentation de vos sous-vues que ne le fournissent les comportements de contrainte ou de redimensionnement automatique.

  • didAddSubview:, willRemoveSubview: Implémentez ces méthodes si nécessaire pour suivre les ajouts et les suppressions de sous-vues.

  • willMoveToSuperview: (, didMoveToSuperview Implémentez ces méthodes si nécessaire pour suivre le mouvement de la vue actuelle dans votre hiérarchie de vue.

  • willMoveToWindow: (, didMoveToWindow Implémentez ces méthodes si nécessaire pour suivre le déplacement de votre vue vers un fenêtre différente.

Gestion des événements:

  • touchesBegan:withEvent:, touchesMoved:withEvent:, touchesEnded:withEvent:, touchesCancelled:withEvent: Implémentez ces méthodes si vous devez gérer directement les événements tactiles. (Pour la saisie basée sur les gestes, utilisez des outils de reconnaissance des gestes.)

  • gestureRecognizerShouldBegin: Implémentez cette méthode si votre vue gère les événements tactiles directement et peut empêcher le système de reconnaissance de mouvements attachés de déclencher des actions supplémentaires.

286

Cela vient toujours en haut dans Google. Vous trouverez ci-dessous un exemple mis à jour pour Swift.

La fonction didLoad vous permet de mettre tout votre code d'initialisation personnalisé. Comme d'autres l'ont mentionné, didLoad sera appelé lors de la création d'une vue par programme via init(frame:) ou lorsque le XIB désérialiseur fusionne un modèle XIB dans votre vue via init(coder:)

De côté: layoutSubviews et updateConstraints sont appelés plusieurs fois pour la majorité des vues. Ceci est destiné aux mises en page multi-passes avancées et aux réglages lorsque les limites d'une vue changent. Personnellement, j’évite autant que possible les mises en page multi-passes, car elles brûlent les cycles du processeur et font tout un mal à la tête. De plus, je mets du code de contrainte dans les initialiseurs eux-mêmes, car je les invalide rarement.

import UIKit

class MyView: UIView {
  //-----------------------------------------------------------------------------------------------------
  //Constructors, Initializers, and UIView lifecycle
  //-----------------------------------------------------------------------------------------------------
  override init(frame: CGRect) {
      super.init(frame: frame)
      didLoad()
  }

  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    didLoad()
  }

  convenience init() {
    self.init(frame: CGRectZero)
  }

  func didLoad() {
    //Place your initialization code here

    //I actually create & place constraints in here, instead of in
    //updateConstraints
  }

  override func layoutSubviews() {
     super.layoutSubviews()

     //Custom manually positioning layout goes here (auto-layout pass has already run first pass)
  }

  override func updateConstraints() {
    super.updateConstraints()

    //Disable this if you are adding constraints manually
    //or you're going to have a 'bad time'
    //self.translatesAutoresizingMaskIntoConstraints = false

    //Add custom constraint code here
  }
}
35
seo

Il y a un bon résumé dans le Apple documentation , et cela est bien décrit dans la version gratuite cours de Stanford disponible sur iTunes. Je présente ma TL. ; Version DR ici:

Si votre classe est principalement composée de sous-vues, le bon endroit pour les allouer est dans les méthodes init. Pour les vues, il existe deux méthodes init différentes qui peuvent être appelées, selon que votre vue est instanciée à partir de code ou d'un nib/storyboard. Ce que je fais, c’est d’écrire ma propre méthode setup, puis de l’appeler à partir de la touche initWithFrame: et initWithCoder: méthodes.

Si vous faites un dessin personnalisé, vous voulez en effet remplacer drawRect: à votre avis. Si votre vue personnalisée est principalement un conteneur pour des sous-vues, vous n'aurez probablement pas besoin de le faire.

Remplacez uniquement layoutSubViews si vous souhaitez ajouter ou supprimer une sous-vue, selon que vous êtes en mode portrait ou paysage. Sinon, vous devriez pouvoir le laisser seul.

14
dpassage

layoutSubviews est destiné à définir le cadre sur les vues enfants, pas sur la vue elle-même.

Pour UIView, le constructeur désigné est généralement initWithFrame:(CGRect)frame et vous devez définir le cadre à cet endroit (ou dans initWithCoder:), éventuellement en ignorant la valeur de trame. Vous pouvez également fournir un constructeur différent et définir le cadre à cet endroit.

1
proxi