Je souhaite utiliser une vue sur plusieurs contrôleurs de vue dans un storyboard. Ainsi, j'ai pensé à concevoir la vue dans un xib externe afin que les changements soient reflétés dans chaque contrôleur de vue. Mais comment charger une vue depuis un xib externe dans un storyboard et est-ce même possible? Si ce n'est pas le cas, quelles autres alternatives sont disponibles pour convenir à la situation ci-dessus?
ios
uiview
storyboard
xib
Sebastian Hoffmann
la source
la source
Réponses:
Mon exemple complet est ici , mais je vais fournir un résumé ci-dessous.
Disposition
Ajoutez un fichier .swift et .xib portant chacun le même nom à votre projet. Le fichier .xib contient votre disposition de vue personnalisée (en utilisant de préférence des contraintes de disposition automatique).
Faites du fichier swift le propriétaire du fichier xib.
Code
Ajoutez le code suivant au fichier .swift et raccordez les prises et les actions du fichier .xib.
Utilise le
Utilisez votre vue personnalisée n'importe où dans votre storyboard. Ajoutez simplement un
UIView
et définissez le nom de la classe sur votre nom de classe personnalisé.la source
init(frame:)
. Consultez ce didacticiel pour plus de détails.Pendant un certain temps, l'approche de Christopher Swasey a été la meilleure que j'ai trouvée. J'ai interrogé quelques développeurs seniors de mon équipe à ce sujet et l'un d'eux avait la solution parfaite ! Cela répond à chacune des préoccupations que Christopher Swasey a abordées avec tant d'éloquence et ne nécessite pas de code de sous-classe standard (ma principale préoccupation avec son approche). Il y a un piège , mais à part cela, il est assez intuitif et facile à mettre en œuvre.
MyCustomClass.swift
MyCustomClass.xib
File's Owner
fichier .xib comme votre classe personnalisée (MyCustomClass
)class
valeur (sous leidentity Inspector
) de votre vue personnalisée dans le fichier .xib. Ainsi, votre vue personnalisée n'aura pas de classe spécifiée, mais elle aura un propriétaire de fichier spécifié.Assistant Editor
.Connections Inspector
vous remarquerez que vos points de vente de référencement ne référencent pas votre classe personnalisée (c'est-à-direMyCustomClass
), mais plutôt une référenceFile's Owner
. Comme ilFile's Owner
est spécifié comme étant votre classe personnalisée, les points de vente se connecteront et fonctionneront proprement.NibLoadable
protocole référencé ci-dessous..swift
nom de fichier de classe personnalisé est différent de votre.xib
nom de fichier, définissez lanibName
propriété sur le nom de votre.xib
fichier.required init?(coder aDecoder: NSCoder)
etoverride init(frame: CGRect)
appelezsetupFromNib()
comme dans l'exemple ci-dessous.MyCustomClass
).Voici le protocole que vous voudrez référencer:
Et voici un exemple d'
MyCustomClass
implémentation du protocole (avec le fichier .xib nomméMyCustomClass.xib
):REMARQUE: Si vous manquez le Gotcha et que vous définissez la
class
valeur à l'intérieur de votre fichier .xib comme votre classe personnalisée, il ne sera pas dessiné dans le storyboard et vous obtiendrez uneEXC_BAD_ACCESS
erreur lorsque vous exécuterez l'application car elle reste bloquée dans une boucle infinie de essayer d'initialiser la classe à partir de la pointe en utilisant lainit?(coder aDecoder: NSCoder)
méthode qui appelleSelf.nib.instantiate
et appelle àinit
nouveau le.la source
setupFromNib()
, semble résoudre certains problèmes étranges de mise en page automatique avec les cellules de vue de tableau à dimensionnement automatique contenant des vues créées par XIB.@IBDesignable
compatibilité. Je ne peux pas croire pourquoi Xcode ou UIKit ne fournit pas quelque chose comme ça par défaut lors de l'ajout d'un fichier UIView.En supposant que vous ayez créé un xib que vous souhaitez utiliser:
1) Créez une sous-classe personnalisée d'UIView (vous pouvez aller dans Fichier -> Nouveau -> Fichier ... -> Cocoa Touch Class. Assurez-vous que "Sous-classe de:" est "UIView").
2) Ajoutez une vue basée sur xib comme sous-vue à cette vue lors de l'initialisation.
Dans Obj-C
Dans Swift 2
Dans Swift 3
3) Où que vous souhaitiez l'utiliser dans votre storyboard, ajoutez un UIView comme vous le feriez normalement, sélectionnez la vue nouvellement ajoutée, allez dans l'inspecteur d'identité (la troisième icône en haut à droite qui ressemble à un rectangle avec des lignes), et entrez le nom de votre sous-classe en tant que «Classe» sous «Classe personnalisée».
la source
xibView.frame = self.frame;
devrait êtrexibView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
, sinon xibView aura un décalage lorsque la vue est ajoutée au storyboard.J'ai toujours trouvé la solution «ajouter comme sous-vue» insatisfaisante, car elle se visse avec (1) la mise en page automatique, (2)
@IBInspectable
et (3) prises. Au lieu de cela, laissez-moi vous présenter la magie d'awakeAfter:
uneNSObject
méthode.awakeAfter
vous permet d'échanger l'objet réellement réveillé d'un NIB / Storyboard avec un objet complètement différent. Cet objet est ensuite soumis au processus d'hydratation, l'aawakeFromNib
appelé, est ajouté en tant que vue, etc.Nous pouvons utiliser ceci dans une sous-classe "découpée en carton" de notre vue, dont le seul but sera de charger la vue depuis le NIB et de la renvoyer pour une utilisation dans le Storyboard. La sous-classe intégrable est ensuite spécifiée dans l'inspecteur d'identité de la vue Storyboard, plutôt que dans la classe d'origine. Il n'est pas nécessaire que ce soit une sous-classe pour que cela fonctionne, mais en faire une sous-classe est ce qui permet à IB de voir toutes les propriétés IBInspectable / IBOutlet.
Ce passe-partout supplémentaire peut sembler sous-optimal - et dans un sens, il l'est, car idéalement
UIStoryboard
le gérerait de manière transparente - mais il a l'avantage de laisser le NIB d'origine et laUIView
sous - classe complètement inchangés. Le rôle qu'il joue est essentiellement celui d'un adaptateur ou d'une classe de pont, et est parfaitement valable, du point de vue de la conception, en tant que classe supplémentaire, même si c'est regrettable. D'un autre côté, si vous préférez être parcimonieux avec vos classes, la solution de @ BenPatch fonctionne en implémentant un protocole avec quelques autres changements mineurs. La question de savoir quelle solution est la meilleure se résume à une question de style programmeur: si l'on préfère la composition d'objets ou l'héritage multiple.Remarque: la classe définie sur la vue dans le fichier NIB reste la même. La sous - classe intégrable est uniquement utilisé dans le story - board. La sous-classe ne peut pas être utilisée pour instancier la vue dans le code, elle ne devrait donc pas avoir de logique supplémentaire elle-même. Il doit seulement contenir le
awakeAfter
crochet.⚠️ Le seul inconvénient majeur ici est que si vous définissez des contraintes de largeur, de hauteur ou de rapport hauteur / largeur dans le storyboard qui ne sont pas liées à une autre vue, elles doivent être copiées manuellement. Les contraintes qui relient deux vues sont installées sur l'ancêtre commun le plus proche, et les vues sont réveillées du storyboard de l'intérieur vers l'extérieur, donc au moment où ces contraintes sont hydratées sur la supervision, l'échange a déjà eu lieu. Les contraintes qui n'impliquent que la vue en question sont installées directement sur cette vue, et sont donc rejetées lorsque l'échange se produit, à moins qu'elles ne soient copiées.
Notez que ce qui se passe ici, c'est que les contraintes installées sur la vue dans le storyboard sont copiées dans la vue nouvellement instanciée , qui peut déjà avoir ses propres contraintes, définies dans son fichier nib. Ceux-ci ne sont pas affectés.
instantiateViewFromNib
est une extension de type sécurisé deUIView
. Tout ce qu'il fait est de parcourir les objets du NIB jusqu'à ce qu'il en trouve un qui correspond au type. Notez que le type générique est la valeur de retour , le type doit donc être spécifié sur le site d'appel.la source
instantiateViewFromNib
ne retournera rien. Pas un gros problème de toute façon IMO, la sous-classe est juste un artifice pour accrocher dans le storyboard, tout le code doit être sur la classe d'origine.MyCustomView
. Dans mon xib, la barre latérale gauche était manquante par défaut; pour l'activer, il y a un bouton à côté du contrôle des traits «Afficher comme: iPhone 7» près du côté inférieur / gauche.La meilleure solution actuellement est d'utiliser simplement un contrôleur de vue personnalisé avec sa vue définie dans un xib, et de supprimer simplement la propriété "view" que Xcode crée à l'intérieur du storyboard lors de l'ajout du contrôleur de vue (n'oubliez pas de définir le nom de la classe personnalisée cependant).
Cela obligera le runtime à rechercher automatiquement le xib et à le charger. Vous pouvez utiliser cette astuce pour tout type de vue de conteneur ou de vue de contenu.
la source
Je pense
alternative
à utiliserXIB views
pour utiliserView Controller
dans un storyboard séparé .Puis , en story - board principal lieu d'utilisation de vue personnalisée
container view
avecEmbed Segue
et ontStoryboardReference
à ce contrôleur de vue personnalisée qui vue doit être placé dans un autre point de vue dans le story - board principale.Ensuite, nous pouvons configurer la délégation et la communication entre ce ViewController intégré et le contrôleur de vue principal via la préparation de segue . Cette approche est différente de celle de l' affichage de UIView, mais beaucoup plus simple et plus efficace (du point de vue de la programmation) peut être utilisée pour atteindre le même objectif, c'est-à-dire avoir une vue personnalisée réutilisable qui est visible dans le storyboard principal
L'avantage supplémentaire est que vous pouvez implémenter votre logique dans la classe CustomViewController et y configurer toute la délégation et la préparation de vue sans créer de classes de contrôleur distinctes (plus difficiles à trouver dans le projet), et sans placer de code standard dans UIViewController principal à l'aide de Component. Je pense que c'est bon pour les composants réutilisables ex. Composant Music Player (comme un widget) qui peut être intégré dans d'autres vues.
la source
Bien que les réponses les plus populaires fonctionnent bien, elles sont conceptuellement erronées. Ils sont tous utilisés
File's owner
comme connexion entre les prises de classe et les composants de l'interface utilisateur.File's owner
est censé être utilisé uniquement pour les objets de niveau supérieur et non pourUIView
s. Consultez le document du développeur Apple . Avoir UIView commeFile's owner
conduit à ces conséquences indésirables.contentView
là où vous êtes censé utiliserself
. Ce n'est pas seulement moche, mais aussi structurellement incorrect car la vue intermédiaire empêche la structure de données de transmettre sa structure d'interface utilisateur. C'est comme l'opposé de l'interface utilisateur déclarative.Il existe une manière élégante de le faire sans utiliser
File's owner
. Veuillez consulter ce billet de blog . Il explique comment procéder de la bonne manière.la source
Voici la réponse que vous vouliez depuis le début. Vous pouvez simplement créer votre
CustomView
classe, en avoir l'instance principale dans un xib avec toutes les sous-vues et sorties. Ensuite, vous pouvez appliquer cette classe à toutes les instances de vos storyboards ou d'autres xibs.Inutile de jouer avec le propriétaire du fichier, de connecter des prises à un proxy ou de modifier le xib d'une manière particulière, ou d'ajouter une instance de votre vue personnalisée en tant que sous-vue d'elle-même.
Faites juste ceci:
UIView
àNibView
(ou deUITableViewCell
àNibTableViewCell
)C'est tout!
Il fonctionne même avec IBDesignable pour référencer votre vue personnalisée (y compris les sous-vues du xib) au moment de la conception dans le storyboard.
Vous pouvez en savoir plus ici: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
Et vous pouvez obtenir le framework open source BFWControls ici: https://github.com/BareFeetWare/BFWControls
Et voici un simple extrait du
NibReplaceable
code qui le pilote, au cas où vous seriez curieux: https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4Tom 👣
la source
Cette solution peut être utilisée même si votre classe n'a pas le même nom que le XIB. Par exemple, si vous avez une classe de contrôleur de vue de base controllerA qui a un nom XIB controllerA.xib et que vous l'avez sous-classée avec controllerB et que vous souhaitez créer une instance de controllerB dans un storyboard, vous pouvez:
*
la source
Solution pour Objective-C selon les étapes décrites dans la réponse de Ben Patch .
Utilisez l'extension pour UIView:
Créer des fichiers
MyView.h
,MyView.m
etMyView.xib
.Préparez d'abord votre
MyView.xib
comme la réponse de Ben Patch le dit, définissez la classeMyView
pour le propriétaire du fichier au lieu de la vue principale dans ce XIB.MyView.h
:MyView.m
:Et plus tard, créez simplement votre vue par programme:
Avertissement! L'aperçu de cette vue ne sera pas affiché dans Storyboard si vous utilisez l'extension WatchKit à cause de ce bogue dans Xcode> = 9.2: https://forums.developer.apple.com/thread/95616
la source