168 votes

Pratique appropriée pour la sous-classification de UIView ?

Je travaille sur des commandes d'entrée personnalisées basées sur UIView, et j'essaie de déterminer la bonne pratique pour configurer la vue. Lorsque l'on travaille avec un UIViewController, il est assez simple d'utiliser l'attribut loadView et autres viewWill , viewDid mais lors de la sous-classification d'un UIView, les méthodes les plus proches dont je dispose sont les suivantes `awakeFromNib , drawRect y layoutSubviews . (Je pense en termes de callbacks de configuration et 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 que je veux qu'elle ait ? (Ma question s'applique indépendamment du fait que j'utilise ou non l'autolayout, bien qu'il puisse y avoir deux réponses). Quelle est la "meilleure pratique" appropriée ?

311voto

Gabriele Petronella Points 32362

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

Consultez la liste ci-dessous, et surtout jetez un coup d'œil à initWithFrame: y layoutSubviews . Le premier est destiné à mettre en place le cadre de votre UIView tandis que le dernier est destiné à configurer le cadre et la disposition de ses sous-vues.

Rappelez-vous également que initWithFrame: est appelé seulement si vous instanciez votre UIView de manière programmatique. Si vous le chargez depuis un fichier nib (ou un storyboard), initWithCoder: sera utilisé. Et dans initWithCoder: le cadre n'a pas encore été calculé, vous ne pouvez donc pas modifier le cadre que vous avez configuré dans le Générateur d'interface. Comme suggéré dans cette réponse vous pouvez penser à appeler initWithFrame: de initWithCoder: afin de mettre en place le cadre.

Enfin, si vous chargez votre UIView à partir d'une plume (ou d'un storyboard), vous disposez également de l'option awakeFromNib l'occasion d'effectuer des initialisations personnalisées du cadre et de la mise en page, puisque lorsque awakeFromNib est appelé, il est garanti que chaque vue de la hiérarchie a été désarchivée et initialisée.

Du doc de NSNibAwaking (désormais remplacée par le doc de awakeFromNib ) :

Les messages destinés aux autres objets peuvent être envoyés en toute sécurité à partir de awakeFromNib - à ce moment-là, il est certain que tous les objets sont désarchivés et initialisés (mais pas nécessairement éveillés, bien sûr).

Il convient également de noter qu'avec 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, afin que le cadre soit automatiquement calculé par le moteur de mise en page.

En direct de la documentation :

Méthodes à 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 de, ou à la place de cette méthode.

  • initWithCoder: Mettez en œuvre cette méthode si vous chargez votre vue à partir d'un fichier nib d'Interface Builder et que votre vue nécessite des fonctions personnalisées. personnalisée.

  • layerClass N'implémentez cette méthode que si vous souhaitez que votre vue utilise une couche Core Animation différente pour son backing store. Par exemple, si vous utilisez OpenGL ES pour faire votre dessin, vous voudriez remplacer cette méthode et renvoyer la classe CAEAGLLayer.

Dessin et impression

  • drawRect: Mettez en œuvre cette méthode si votre vue dessine un contenu personnalisé. Si votre vue ne fait pas de dessin personnalisé, évitez de surcharger cette méthode méthode.

  • drawRect:forViewPrintFormatter: Ne mettez en œuvre cette méthode que si vous souhaitez dessiner le contenu de votre vue différemment lors de l'impression.

Contraintes

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

  • updateConstraints Mettez en œuvre cette méthode si votre vue doit créer des contraintes personnalisées entre vos sous-vues.

  • alignmentRectForFrame: , frameForAlignmentRect: Mettez en œuvre ces méthodes pour modifier la façon dont vos vues sont alignées sur d'autres vues.

Mise en page

  • sizeThatFits: Mettez en œuvre 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. de redimensionnement. Par exemple, vous pouvez utiliser cette méthode pour empêcher votre vue vue de rétrécir au point que les sous-vues ne puissent pas être affichées correctement.

  • layoutSubviews Mettez en œuvre cette méthode si vous avez besoin d'un contrôle plus précis de la disposition de vos sous-vues que les méthodes de contrainte ou de contrôle de l'accès. ou le comportement de redimensionnement automatique.

  • didAddSubview: , willRemoveSubview: Mettez en œuvre ces méthodes si nécessaire pour suivre les ajouts et suppressions de sous-vues.

  • willMoveToSuperview: , didMoveToSuperview Mettez en œuvre ces méthodes selon les besoins pour suivre le mouvement de la vue actuelle dans votre vue hiérarchie.

  • willMoveToWindow: , didMoveToWindow Mettez en œuvre ces méthodes si nécessaire pour suivre le déplacement de votre vue vers une autre fenêtre.

Gestion des événements :

  • touchesBegan:withEvent: , touchesMoved:withEvent: , touchesEnded:withEvent: , touchesCancelled:withEvent: Mettre en œuvre ces méthodes si vous devez gérer directement les événements de contact. (Pour entrée basée sur les gestes, utilisez des reconnaisseurs de gestes).

  • gestureRecognizerShouldBegin: Mettez en œuvre cette méthode si votre vue gère directement les événements de contact et que vous souhaitez empêcher les événements de contact attachés. de déclencher des actions supplémentaires.

41voto

seo Points 177

Il apparaît toujours en bonne place dans Google. Vous trouverez ci-dessous un exemple actualisé pour le martinet.

Le site didLoad vous permet de placer tout votre code d'initialisation personnalisé. Comme d'autres l'ont mentionné, didLoad sera appelé lorsqu'une vue est créée de manière programmatique via init(frame:) ou lorsque le XIB Le désérialiseur fusionne un XIB dans votre vue via init(coder:)

Aside : layoutSubviews y updateConstraints sont appelés plusieurs fois pour la majorité des vues. Ceci est destiné aux mises en page avancées à passages multiples et aux ajustements lorsque les limites d'une vue changent. Personnellement, j'évite les mises en page à passages multiples dans la mesure du possible, car elles brûlent les cycles du processeur et font de tout un casse-tête. En outre, je place le code de contrainte dans les initialisateurs 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
  }
}

14voto

dpassage Points 2665

Il y a un bon résumé dans l'Apple documentation Cette question est bien traitée dans le document gratuit Cours de Stanford disponible sur iTunes. Je présente ici ma version TL;DR :

Si votre classe se compose principalement de sous-vues, le bon endroit pour les allouer est dans le fichier init méthodes. Pour les vues, il existe deux méthodes différentes init méthodes qui pourraient être appelées, selon que votre vue est instanciée depuis le code ou depuis une nib/storyboard. Ce que je fais, c'est écrire mon propre setup puis l'appeler à partir de la méthode initWithFrame: y initWithCoder: méthodes.

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

Contourner uniquement layoutSubViews si vous voulez faire quelque chose comme ajouter ou supprimer une vue secondaire selon que vous êtes en orientation portrait ou paysage. Sinon, vous devriez pouvoir le laisser tranquille.

1voto

proxi Points 744

layoutSubviews est destiné à définir le cadre sur les vues enfant, et non 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 le fichier initWithCoder: ), en ignorant éventuellement la valeur de la trame passée. Vous pouvez également fournir un constructeur différent et y définir le cadre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X