158 votes

Charger une vue à partir d'un fichier xib externe dans le storyboard

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 ?

1 votes

Regardez cette vidéo: youtube.com/watch?v=o3MawJVxTgk

7voto

Michał Ziobro Points 1357

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.

6voto

Ben G Points 2106

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.

1voto

Ariel Bogdziewicz Points 126

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

1voto

barefeettom Points 41

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:

  1. Importer le framework BFWControls
  2. Changez votre superclasse de UIView à NibView (ou de UITableViewCell à 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

0voto

otusweb Points 395

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];
       }
}

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