web-dev-qa-db-fra.com

Comment charger un UIView à l'aide d'un fichier nib créé avec Interface Builder

J'essaie de faire quelque chose d'un peu complexe, mais quelque chose qui devrait être possible. Voici donc un défi pour tous vos experts (ce forum regroupe beaucoup d'entre vous :)).

Je crée un "composant" Questionnaire que je souhaite charger sur un NavigationContoller (mon QuestionManagerViewController). Le "composant" est un "vide" UIViewController, qui peut charger différentes vues en fonction de la question à laquelle il faut répondre.

La façon dont je le fais est:

  1. Créez un objet Question1View en tant que sous-classe UIView, définissant des éléments IBOutlets.
  2. Créez (à l'aide d'Interface Builder) le Question1View.xib(ICI IS O MON PROBLÈME EST PROBABLEMENT). Je règle à la fois le UIViewController et le UIView de classe Question1View.
  3. Je lie les points de vente avec le composant de la vue (en utilisant IB).
  4. Je remplace le initWithNib de mon QuestionManagerViewController pour ressembler à ceci:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
        if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
            // Custom initialization
        }
        return self;
    }
    

Lorsque je lance le code, j'obtiens cette erreur:

2009-05-14 15: 05: 37.152 iMobiDines [17148: 20b] *** Application terminée en raison d'une exception non interceptée 'NSInternalInconsistencyException', raison: '-[UIViewController _loadViewFromNibNamed:bundle:] a chargé la plume "Question1View", mais la vue sortie n'a pas été réglé.

Je suis sûr qu'il existe un moyen de charger la vue à l'aide du fichier nib sans avoir à créer une classe viewController.

238
Gonso

Il existe également un moyen plus simple d’accéder à la vue au lieu de traiter la nib en tant que tableau.

1) Créez une sous-classe View personnalisée avec les prises auxquelles vous souhaitez accéder ultérieurement. --Mon avis

2) dans l'UIViewController que vous voulez charger et gérer le nib, créez une propriété IBOutlet qui contiendra la vue du nib chargé, par exemple

dans MyViewController (une sous-classe UIViewController)

  @property (nonatomic, retain) IBOutlet UIView *myViewFromNib;

(n'oubliez pas de le synthétiser et de le publier dans votre fichier .m)

) ouvrez votre nib (nous l'appellerons 'myViewNib.xib') dans IB, définissez le propriétaire de votre fichier sur MyViewController

4) connectez maintenant la sortie propriétaire de votre fichier myViewFromNib à la vue principale de la nib.

5) Maintenant dans MyViewController, écrivez la ligne suivante:

[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];

Désormais, en appelant votre propriété "self.myViewFromNib", vous aurez accès à la vue depuis votre nib!

223
averydev

Merci à tous. J'ai trouvé un moyen de faire ce que je voulais.

  1. Créez votre UIView avec le IBOutlets dont vous avez besoin.
  2. Créez la bibliothèque xib dans IB, concevez-la à votre convenance et associez-la comme suit: Le propriétaire du fichier est de classe UIViewController (aucune sous-classe personnalisée, mais la "vraie"). La vue du propriétaire du fichier est connectée à la vue principale et sa classe est déclarée comme étant celle de l'étape 1).
  3. Connectez vos commandes avec le IBOutlets.
  4. La DynamicViewController peut exécuter sa logique pour décider quelle vue/xib charger. Une fois sa décision prise, dans la méthode loadView, mettez quelque chose comme ceci:

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
                                                      owner:self
                                                    options:nil];
    
    QPickOneView* myView = [ nibViews objectAtIndex: 1];
    
    myView.question = question;
    

C'est ça!

La méthode loadNibNamed du paquet principal se charge d'initialiser la vue et de créer les connexions.

Maintenant, le ViewController peut afficher une vue ou une autre en fonction des données en mémoire, et l'écran "parent" n'a pas besoin de se soucier de cette logique.

119
Gonso

Je ne suis pas sûr de ce que certaines des réponses parlent, mais je dois mettre cette réponse ici pour ma prochaine recherche dans Google. Mots-clés: "Comment charger un UIView depuis un nib" ou "Comment charger un UIView depuis un NSBundle."

Voici le code presque 100% directement depuis Apress début iPhone Livre (page 247, "Utilisation de la nouvelle cellule du tableau"):

- (void)viewDidLoad {
    [super viewDidLoad];
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
                                                 owner:self options:nil];
    Blah *blah;
    for (id object in bundle) {
        if ([object isKindOfClass:[Blah class]]) {
            blah = (Blah *)object;
            break;
        }
    }   
    assert(blah != nil && "blah can't be nil");
    [self.view addSubview: blah];
} 

Cela suppose que vous avez une sous-classe UIView appelée Blah, une pointe appelée Blah contenant un UIView dont la classe est définie sur Blah.


Catégorie: NSObject + LoadFromNib

#import "NSObject+LoadFromNib.h"

@implementation NSObject (LoadFromNib)

+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:classToLoad]) {
            return object;
        }
    }
    return nil;
}

@end

Extension rapide

extension UIView {
    class func loadFromNib<T>(withName nibName: String) -> T? {
        let nib  = UINib.init(nibName: nibName, bundle: nil)
        let nibObjects = nib.instantiate(withOwner: nil, options: nil)
        for object in nibObjects {
            if let result = object as? T {
                return result
            }
        }
        return nil
    }
}

Et un exemple d'utilisation:

class SomeView: UIView {
    class func loadFromNib() -> SomeView? {
        return self.loadFromNib(withName: "SomeView")
    }
}
69
Dan Rosenstark

Pour tous ceux qui ont besoin de gérer plus d'une instance de la vue personnalisée, il s'agit d'une Outlet Collection , j'ai fusionné et personnalisé les @Gonso, @AVeryDev. et @Olie répond de cette manière:

  1. Créez un MyView : UIView personnalisé et définissez-le comme " Classe personnalisée " de la racine UIView dans l'élément souhaité . _ xib _ custom class

  2. Créez toutes les sorties dont vous avez besoin dans MyView (faites-le maintenant car après le point 3, l'IB vous proposera de connecter des sorties à la UIViewController et non à la vue personnalisée que nous voulons); custom class outlet

  3. Définissez votre UIViewController comme " Propriétaire du fichier " de la vue personnalisée XIBenter image description here

  4. Dans le UIViewController ajoutez un nouveau UIViews pour chaque instance de MyView souhaitée et connectez-les à UIViewController en créant une collection de prises : ces vues agiront comme des vues "enveloppantes" pour les instances de vue personnalisées; enter image description here

  5. Enfin, dans la viewDidLoad de votre UIViewController ajoutez les lignes suivantes:

NSArray *bundleObjects;
MyView *currView;
NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count];
for (UIView *currWrapperView in myWrapperViews) {
    bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
    for (id object in bundleObjects) {
        if ([object isKindOfClass:[MyView class]]){
            currView = (MyView *)object;
            break;
        }
    }

    [currView.myLabel setText:@"myText"];
    [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal];
    //...

    [currWrapperView addSubview:currView];
    [myViews addObject:currView];
}
//self.myViews = myViews; if need to access them later..
22
Francesco Vadicamo

J'utiliserais UINib pour instancier une UIView personnalisée pour qu'elle soit réutilisée

UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];

Les fichiers nécessaires dans ce cas sont MyCustomView.xib, MyCustomViewClass.h et MyCustomViewClass.m Notez que [UINib instantiateWithOwner] renvoie un tableau, donc vous devez utiliser l'élément qui reflète UIView que vous souhaitez réutiliser. Dans ce cas, c'est le premier élément.

18
thgc

Vous ne devez pas définir la classe de votre contrôleur de vue comme une sous-classe de UIView dans Interface Builder. C'est très certainement au moins une partie de votre problème. Laissez-la soit comme UIViewController, soit une sous-classe de celle-ci, ou une autre classe personnalisée que vous avez.

En ce qui concerne le chargement d’une vue à partir d’un fichier xib, j’étais supposé que vous aviez d’avoir une sorte de contrôleur de vue (même s’il n’étend pas UIViewController, ce qui est peut-être trop poids lourd pour vos besoins) défini comme propriétaire du fichier dans Interface Builder si vous souhaitez l'utiliser pour définir votre interface. J'ai aussi fait quelques recherches pour le confirmer. En effet, sinon, il n’y aurait aucun moyen d’accéder aux éléments d’interface de la variable UIView et il n’y aurait pas non plus moyen de faire en sorte que vos propres méthodes dans le code soient déclenchées par des événements.

Si vous utilisez un UIViewController comme propriétaire de votre fichier pour vos vues, vous pouvez simplement utiliser initWithNibName:bundle: pour le charger et récupérer l'objet du contrôleur de vue. Dans IB, assurez-vous de définir la sortie view sur la vue avec votre interface dans le fichier xib. Si vous utilisez un autre type d'objet en tant que propriétaire de votre fichier, vous devez utiliser la méthode loadNibNamed:owner:options: de NSBundle pour charger le nib, en transmettant une instance de File's Owner à la méthode. Toutes ses propriétés seront définies correctement en fonction des prises que vous définissez dans IB.

11
Marc W

Vous pouvez également utiliser initWithNibName de UIViewController au lieu de loadNibNamed. C'est plus simple, je trouve.

UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release];  // release the VC

Il ne vous reste plus qu'à créer MySubView.xib et MySubView.h/m. Dans MySubView.xib, définissez la classe Propriétaire du fichier sur UIViewController et affichez la classe sur MySubView.

Vous pouvez positionner et la taille de subview à l'aide du fichier xib parent.

10
Ying

C'est quelque chose qui devrait être plus facile. J'ai fini par étendre UIViewController et ajouter un sélecteur loadNib:inPlaceholder:. Maintenant je peux dire

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

Voici le code pour la catégorie (il fait le même rigamarole que décrit par Gonso):

@interface UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;

@end

@implementation UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName
{
  NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; 
  for (id view in xib) { // have to iterate; index varies
    if ([view isKindOfClass:[UIView class]]) return view;
  }
  return nil;
}

- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
  UIView *nibView = [self viewFromNib:nibName];
  [nibView setFrame:placeholder.frame];
  [self.view insertSubview:nibView aboveSubview:placeholder];
  [placeholder removeFromSuperview];
  return nibView;
}

@end
8
skot

C’est une excellente question (+1) et les réponses étaient presque utile;) Désolé les gars, mais j’ai passé un sacré moment à passer à travers cela, même si Gonso et AVeryDev ont tous deux donné de bons conseils. Espérons que cette réponse aidera les autres.

MyVC est le contrôleur de vue contenant tout cela.

MySubview est la vue que nous voulons charger depuis un xib

  • Dans MyVC.xib, créez une vue de type MySubView ayant la bonne taille, la bonne forme et la position souhaitée.
  • Dans MyVC.h, ont

    IBOutlet MySubview *mySubView
    // ...
    @property (nonatomic, retain) MySubview *mySubview;
    
  • Dans MyVC.m, @synthesize mySubView; et n'oubliez pas de le publier dans dealloc.

  • Dans MySubview.h, disposez d'un point de vente/propriété pour UIView *view (peut être inutile, mais cela a fonctionné pour moi.) Synthétisez et publiez-le au format .m
  • Dans MySubview.xib
    • définissez le type de propriétaire de fichier sur MySubview et liez la propriété view à votre vue.
    • Disposez tous les bits et connectez-les comme vous le souhaitez à la IBOutlet
  • De retour dans MyVC.m, ont

    NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL];
    MySubview *msView = [xibviews objectAtIndex: 0];
    msView.frame = mySubview.frame;
    UIView *oldView = mySubview;
    // Too simple: [self.view insertSubview: msView aboveSubview: mySubview];
    [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting
    self.mySubview = msView;
    [oldCBView removeFromSuperview];
    

Le problème le plus difficile pour moi était le suivant: les astuces des autres réponses chargeaient ma vue de xib, mais ne remplaçaient PAS celle de MyVC (duh!) - je devais la changer moi-même.

De plus, pour accéder aux méthodes de mySubview, la propriété view du fichier .xib doit être définie sur MySubview. Sinon, cela revient comme un UIView ordinaire.

S'il y a un moyen de charger mySubview directement à partir de sa propre xib, c'est génial, mais cela me conduit là où je devais être.

8
Olie

Moi aussi je voulais faire quelque chose de similaire, voici ce que j'ai trouvé: (SDK 3.1.3)

J'ai un contrôleur de vue A (lui-même appartenant à un contrôleur de navigation) qui charge VC B en appuyant sur un bouton:

Dans AViewController.m

BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];

Maintenant, VCB a son interface depuis Bnib, mais lorsqu'un bouton est enfoncé, je souhaite passer à un 'mode édition' disposant d'une interface distincte, mais je ne souhaite pas changer de mode. VC pour le mode édition, je souhaite que la nouvelle plume soit associée à mon VC existant.

Donc, dans BViewController.m (dans la méthode de pression de bouton)

NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];

Puis, sur un autre bouton, appuyez sur (pour quitter le mode édition):

[editView removeFromSuperview];

et je reviens à mon original Bnib.

Cela fonctionne bien, mais notez que mon EditMode.nib ne contient qu’un seul obj de niveau supérieur, un obj UIView. Peu importe que le propriétaire du fichier dans ce nib soit défini en tant que BViewController ou que NSObject par défaut, MAIS assurez-vous que la vue View Outlet dans le propriétaire du fichier N'EST PAS définie. Si c'est le cas, alors je reçois un crash exc_bad_access et xcode continue à charger 6677 cadres de pile montrant une méthode UIView interne appelée à plusieurs reprises ... ressemble donc à une boucle infinie. (Le View Outlet IS défini dans mon Bnib original cependant)

J'espère que cela t'aides.

6
Brynjar

dans Swift

En fait, ma solution à ce problème était de charger la vue dans un viewDidLoad dans mon CustonViewController où je voulais utiliser la vue comme ça:

myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView

Ne chargez pas la vue dans une méthode loadView()! La méthode loadView sert au chargement de la vue pour votre ViewController personnalisé.

6
kalafun

J'ai fait une catégorie que j'aime bien:

UIView+NibInitializer.h

#import <UIKit/UIKit.h>

@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end

UIView+NibInitializer.m

#import "UIView+NibInitializer.h"

@implementation UIView (NibInitializer)

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    if (!nibNameOrNil) {
        nibNameOrNil = NSStringFromClass([self class]);
    }
    NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
                                                        owner:self
                                                      options:nil];
    for (id view in viewsInNib) {
        if ([view isKindOfClass:[self class]]) {
            self = view;
            break;
        }
    }
    return self;
}

@end

Ensuite, appelez comme ceci:

MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];

Utilisez un nom de plume si votre plume porte un nom autre que le nom de votre classe.

Pour le remplacer dans vos sous-classes pour un comportement supplémentaire, cela pourrait ressembler à ceci:

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    self = [super initWithNibNamed:nibNameOrNil];
    if (self) {
        self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
    }
    return self;
}
6
Logan

Pour Swift utilisateur avec option configurable:

  1. Créez une sous-classe UIView personnalisée et un fichier xib , que nous nommerons d'après notre propre nom de classe: dans notre cas, MemeView. Dans la classe Meme View, n'oubliez pas de la définir comme désignable avec l'attribut @IBDesignable avant la déclaration de classe.
  2. Rappelez-vous de définir le propriétaire du fichier dans xib avec notre sous-classe personnalisée UIView dans le panneau Inspecteur Indetity

    enter image description here

  3. Dans le fichier xib, nous pouvons maintenant construire notre interface, créer des contraintes, créer des points de vente, des actions, etc. enter image description here

  4. Nous devons implémenter quelques méthodes dans notre classe personnalisée pour ouvrir le fichier xib une fois initialisé.

    class XibbedView: UIView {

    weak var nibView: UIView!
    
    override convenience init(frame: CGRect) {
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        self.init(nibName: nibName)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    init(nibName: String) {
        super.init(frame: CGRectZero)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    func setUpConstraints() {
        ["V","H"].forEach { (quote) -> () in
            let format = String(format:"\(quote):|[nibView]|")
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView]))
        }
    }
    
    func loadNib(name: String) -> UIView {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: name, bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
    
        return view
    }
    

    }

  5. Dans notre classe personnalisée, nous pouvons également définir certaines propriétés pouvant être inspectées pour les contrôler entièrement depuis le constructeur d'interface.

    @IBDesignable class MemeView: XibbedView {

    @IBInspectable var memeImage: UIImage = UIImage() {
        didSet {
            imageView.image = memeImage
        }
    }
    @IBInspectable var textColor: UIColor = UIColor.whiteColor() {
        didSet {
            label.textColor = textColor
        }
    }
    @IBInspectable var text: String = "" {
        didSet {
            label.text = text
        }
    }
    @IBInspectable var roundedCorners: Bool = false {
        didSet {
            if roundedCorners {
                layer.cornerRadius = 20.0
                clipsToBounds = true
            }
            else {
                layer.cornerRadius = 0.0
                clipsToBounds = false
            }
        }
    }
    
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var imageView: UIImageView!
    

}

Quelques exemples:
enter image description hereenter image description here

Si nous avons besoin d'ajouter plus d'informations si la vue est affichée dans un storyboard ou un autre xib, nous pouvons pour cela implémenter prepareForInterfaceBuilder(), mais cette méthode ne sera exécutée que lors de l'ouverture du fichier dans le générateur d'interface. Si vous avez fait tout ce que j'ai écrit mais que rien ne fonctionne, c'est un moyen de déboguer une vue sigle en ajoutant des points d'arrêt dans son implémentation. enter image description here
Voici la hiérarchie des vues. View hiearchy

J'espère que cela vous aidera à télécharger un échantillon complet ici

5
Andrea

J'ai trouvé cette publication sur le blog par Aaron Hillegass (auteur, instructeur, Cocoa ninja) très éclairant. Même si vous n'adoptez pas son approche modifiée pour charger les fichiers NIB via un initialiseur désigné, vous aurez probablement au moins une meilleure compréhension du processus en cours. J'utilise cette méthode récemment avec beaucoup de succès!

4
Meltemi

J'avais des raisons de faire la même chose (chargement par programme d'une vue à partir d'un fichier XIB), mais je devais le faire entièrement à partir du contexte d'une sous-classe d'une sous-classe d'un UIView (c'est-à-dire sans impliquer le contrôleur de vue dans en tous cas). Pour ce faire, j'ai créé cette méthode utilitaire:

+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {

    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
                                                    owner:myself options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:[myself class]]) {
            return object;
        }
    }  

    return nil;
} 

Ensuite, je l’appelle depuis ma sous-classe 'initWithFrame comme ceci:

- (id)initWithFrame:(CGRect)frame {

    self = [Utilities initWithNibName:@"XIB1" withSelf:self];
    if (self) {
        // Initialization code.
    }
    return self;
}

Posté pour l'intérêt général; si quelqu'un voit des problèmes sans le faire de cette façon, s'il vous plaît faites le moi savoir.

3
MusiGenesis

Voici un moyen de le faire dans Swift (en train d'écrire Swift 2.0 dans XCode 7 beta 5).

Depuis votre sous-classe UIView que vous avez définie comme "Classe personnalisée" dans le Générateur d'interface, créez une méthode comme celle-ci (ma sous-classe est appelée RecordingFooterView):

class func loadFromNib() -> RecordingFooterView? {
    let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
    let nibObjects = nib.instantiateWithOwner(nil, options: nil)
    if nibObjects.count > 0 {
        let topObject = nibObjects[0]
        return topObject as? RecordingFooterView
    }
    return nil
}

Ensuite, vous pouvez simplement l'appeler comme ceci:

let recordingFooterView = RecordingFooterView.loadFromNib()

3
Johannes

La réponse précédente ne prend pas en compte un changement de la structure NIB (XIB) survenu entre la version 2.0 et la version 2.1 du kit SDK pour iPhone. Le contenu de l'utilisateur commence maintenant à l'index 0 au lieu de 1.

Vous pouvez utiliser la macro 2.1 valable pour toutes les versions 2.1 et supérieures (deux soulignements avant IPHONE:

 // Cited from previous example
 NSArray* nibViews =  [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
 int startIndex;
 #ifdef __IPHONE_2_1
 startIndex = 0;
 #else
 startIndex = 1;
 #endif
 QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
 myView.question = question;

Nous utilisons une technique similaire à celle-ci pour la plupart de nos applications.

Barney

3
Barney Mattox

Aucune des réponses n’explique comment créer le fichier XIB autonome à la base de cette question. Il n'y a pas d'option Xcode 4 pour "Créer un nouveau fichier XIB".

Pour faire ça

1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format

Cela peut sembler simple mais cela peut vous éviter quelques minutes de fouilles car le mot "XIB" n'apparaît nulle part.

2
whitneyland

@AVeryDev

6) Pour attacher la vue chargée à la vue de votre contrôleur de vue:

[self.view addSubview:myViewFromNib];

Vraisemblablement, il est nécessaire de le supprimer de la vue pour éviter les fuites de mémoire.

Pour clarifier: le contrôleur de vue a plusieurs IBOutlets, dont certains sont connectés aux éléments du fichier nib d'origine (comme d'habitude) et d'autres à des éléments du nib chargé. Les deux nibs ont la même classe de propriétaire. La vue chargée recouvre celle d'origine.

Astuce: définissez l'opacité de la vue principale dans la plume chargée sur zéro afin qu'elle ne masque pas les éléments de la plume d'origine.

1
Matt

Version la plus courte:

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];
1
wzbozon

Après avoir passé de nombreuses heures, j'ai forgé la solution suivante. Suivez ces étapes pour créer un UIView personnalisé.

1) Créer une classe myCustomView héritée de UIView .
enter image description here

2) Créez . Xib avec le nom myCustomView .
enter image description here

3) Modifiez la classe de UIView dans votre fichier .xib, attribuez myCustomView Classe là-bas.
enter image description here

4) Créer IBOutlets
enter image description here

5) Chargez .xib dans myCustomView * customView . Utilisez l'exemple de code suivant.

myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];

Remarque: pour ceux qui ont toujours des problèmes à commenter, je leur fournirai un lien vers un exemple de projet avec myCustomView

1
Aamir

J'ai l'habitude de nommer xibs avec des vues identiques à la vue. Comme on le ferait pour un contrôleur de vue. Ensuite, je n'ai pas à écrire les noms de classe dans le code. Je charge un UIView depuis un fichier nib portant le même nom.

Exemple pour une classe appelée MyView.

  • Créez un fichier nib appelé MyView.xib dans Interface Builder.
  • Dans Interface Builder, ajoutez un UIView. Définissez sa classe sur MyView. Personnalisez le contenu de votre coeur, connectez les variables d'instance de MyView à des sous-vues auxquelles vous voudrez peut-être accéder ultérieurement.
  • Dans votre code, créez un nouveau MyView comme ceci:

    MyView * myView = [MyView nib_viewFromNibWithOwner: propriétaire];

Voici la catégorie pour cela:

@implementation UIView (nib)

+ (id) nib_viewFromNib {
    return [self nib_viewFromNibWithOwner:nil];
}

+ (id) nib_viewFromNibWithOwner:(id)owner {
    NSString *className = NSStringFromClass([self class]);
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
    UIView *view = nil;
    for(UIView *v in nib) {
        if ([v isKindOfClass:[self class]]) {
            view = v;
            break;
        }
    }
    assert(view != nil && "View for class not found in nib file");
    [view nib_viewDidLoad];
    return view;
}

// override this to do custom setup
-(void)nib_viewDidLoad {

}

Ensuite, je câblais des boutons avec les actions du contrôleur que j'utilise et définissais les choses sur les étiquettes à l'aide des prises de ma sous-classe de vues personnalisées.

0
n13

Pour charger par programme une vue depuis un nib/xib dans Swift 4:

// Load a view from a Nib given a placeholder view subclass
//      Placeholder is an instance of the view to load.  Placeholder is discarded.
//      If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {

    let nib = loadNib(givenNibName, placeholder: placeholderView)
    return instantiateView(fromNib: nib, placeholder: placeholderView)
}

// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
    //1. Load and unarchive nib file
    let nibName = givenNibName ?? String(describing: type(of: placeholderView))

    let nib = UINib(nibName: nibName, bundle: Bundle.main)
    return nib
}

// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
    //1. Get top level objects
    let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)

    //2. Have at least one top level object
    guard let firstObject = topLevelObjects.first else {
        fatalError("\(#function): no top level objects in nib")
    }

    //3. Return instantiated view, placeholderView is not used
    let instantiatedView = firstObject as! T
    return instantiatedView
}
0
Eng Yew