Je veux utiliser une vue à travers plusieurs viewcontrollers dans un storyboard. Ainsi, j'ai pensé à concevoir la vue dans un xib externe afin que les changements soient reflétés dans chaque viewcontroller. Mais comment peut-on charger une vue à partir d'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 répondre à la situation ci-dessus ?
Réponses
Trop de publicités?Je réfléchis à des alternatives
pour utiliser des vues XIB
en utilisant View Controller
dans un storyboard séparé.
Ensuite, dans le storyboard principal, au lieu d'utiliser une vue personnalisée, utilisez une vue de conteneur
avec un Embed Segue
et avoir une Référence au Storyboard
vers ce contrôleur de vue personnalisée dont la vue doit être placée à l'intérieur d'une autre vue dans le storyboard principal.
Ensuite, nous pouvons configurer la délégation et la communication entre ce ViewController intégré et le contrôleur de vue principal à travers prepare for segue. Cette approche est différente de l'affichage d'une UIView, mais beaucoup plus simple et 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 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 la vue sans créer des classes de contrôleurs séparées (plus difficiles à trouver dans le projet) et sans placer de code inutile dans le UIViewController principal en utilisant un composant. Je pense que c'est bon pour les composants réutilisables ex. un composant Lecteur de musique (sous forme de widget) qui peut être incorporé dans d'autres vues.
La meilleure solution actuelle est simplement d'utiliser un contrôleur de vue personnalisé avec sa vue définie dans un fichier xib, et 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 permettra à l'exécution de chercher automatiquement le xib et de le charger. Vous pouvez utiliser cette astuce pour tout type de vues de conteneur ou de vues de contenu.
Solution pour Objective-C selon les étapes décrites dans la réponse de Ben Patch.
Utilisez l'extension pour UIView:
@interface UIView (NibLoadable)
- (UIView*)loadFromNib
{
UIView *xibView = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject];
xibView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:xibView];
[xibView.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
[xibView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES;
[xibView.leftAnchor constraintEqualToAnchor:self.leftAnchor].active = YES;
[xibView.rightAnchor constraintEqualToAnchor:self.rightAnchor].active = YES;
return xibView;
}
@end
Créez les fichiers MyView.h
, MyView.m
et MyView.xib
.
Préparez d'abord votre MyView.xib
comme le dit la réponse de Ben Patch, en définissant la classe MyView
pour le propriétaire du fichier au lieu de la vue principale à l'intérieur de ce XIB.
MyView.h
:
#import
IB_DESIGNABLE @interface MyView : UIView
@property (nonatomic, weak) IBOutlet UIView* someSubview;
@end
MyView.m
:
#import "MyView.h"
#import "UIView+NibLoadable.h"
@implementation MyView
#pragma mark - Initializers
- (id)init
{
self = [super init];
if (self) {
[self loadFromNib];
[self internalInit];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self loadFromNib];
[self internalInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self loadFromNib];
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self internalInit];
}
- (void)internalInit
{
// Initialisation personnalisée.
}
@end
Et plus tard, créez simplement votre vue de manière programmatique:
MyView* view = [[MyView alloc] init];
Attention! L'aperçu de cette vue ne sera pas affiché dans Storyboard si vous utilisez l'extension WatchKit en raison de ce bogue dans Xcode >= 9.2: https://forums.developer.apple.com/thread/95616
Voici la réponse que vous avez tant recherchée. Vous pouvez simplement créer votre classe CustomView
, avoir une instance principale de celle-ci dans un xib avec toutes les sous-vues et les outlets. Ensuite, vous pouvez appliquer cette classe à n'importe quelle instance dans vos storyboards ou autres xibs.
Pas besoin de jouer avec le propriétaire du fichier, ou de connecter les outlets à 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 simplement ceci:
- Importer le framework BFWControls
- Changez votre superclasse de
UIView
àNibView
(ou deUITableViewCell
àNibTableViewCell
)
C'est tout!
Cela fonctionne même avec IBDesignable pour faire référence à votre vue personnalisée (y compris les sous-vues du xib) au moment de la conception dans le storyboard.
Vous pouvez en savoir plus à ce sujet 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 code NibReplaceable
qui le pilote, au cas où vous seriez curieux :
https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4
Tom
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 de XIB controllerA.xib et que vous avez sous-classé cela avec controllerB et que vous souhaitez créer une instance de controllerB dans un storyboard, alors vous pouvez :
- créer le contrôleur de vue dans le storyboard
- définir la classe du contrôleur sur controllerB
- supprimer la vue du controllerB dans le storyboard
- remplacer load view dans controllerA par :
*
- (void) loadView
{
//selon la documentation, si un nibName a été passé dans initWithNibName ou
//ce contrôleur a été créé à partir d'un storyboard (et que le contrôleur a une vue), alors le nibname sera défini
//sinon il sera nul
if (self.nibName)
{
//un nib a été spécifié, respectez cela
[super loadView];
}
else
{
//si pas de nom de nib, essayez d'abord un nib portant le même nom que la classe
//si cela échoue, forcer le chargement à partir du nib de la classe de base
//ceci est pratique pour inclure une sous-classe de ce contrôleur
//dans un storyboard
NSString *className = NSStringFromClass([self class]);
NSString *pathToNIB = [[NSBundle bundleForClass:[self class]] pathForResource: className ofType:@"nib"];
UINib *nib ;
if (pathToNIB)
{
nib = [UINib nibWithNibName: className bundle: [NSBundle bundleForClass:[self class]]];
}
else
{
//forcer le chargement depuis le nib afin que toutes les sous-classes aient le bon xib
//ceci est pratique pour inclure une sous-classe
//dans un storyboard
nib = [UINib nibWithNibName: @"baseControllerXIB" bundle:[NSBundle bundleForClass:[self class]]];
}
self.view = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
}
}
2 votes
Possible duplicate de Comment créer une classe de vue iOS personnalisée et instancier plusieurs copies de celle-ci (dans IB) ?
1 votes
Regardez cette vidéo: youtube.com/watch?v=o3MawJVxTgk