web-dev-qa-db-fra.com

iOS loadNibNamed confusion, quelle est la meilleure pratique?

Je connais la plupart du processus de création d'un XIB pour ma propre sous-classe UIView, mais tout ne fonctionne pas correctement pour moi - c'est principalement à cause de la connexion des IBOutlets. Je peux les amener à travailler de manière apparemment détournée.

Ma configuration est la suivante:

  • J'ai MyClass.h et MyClass.m. Ils ont des IBOutlets pour une UIView (appelée vue) et une UILabel (appelée myLabel). J'ai ajouté la propriété 'view' parce que certains exemples en ligne semblaient suggérer que vous en avez besoin, et cela a en fait résolu un problème où je recevais un crash car il ne pouvait pas trouver la propriété view, je suppose que même pas dans la classe parent UIView .
  • J'ai un fichier XIB appelé MyClass.xib, et la classe personnalisée de son propriétaire de fichier est MyClass, qui s'est pré-remplie correctement après que mes .h et .m pour cette classe aient existé.

Ma méthode d'init est l'endroit où j'ai des problèmes.

J'ai essayé d'utiliser la méthode 'loadNibNamed' de NSBundle mainBundle et de définir le propriétaire sur 'self', en espérant que je créerais une instance de la vue et que ses sorties seraient automatiquement adaptées à celles de ma classe (je sais comment faire cela et je suis prudent). J'ai alors pensé que je voudrais faire "moi" égal à la sous-vue à l'index 0 dans cette plume, plutôt que de le faire

self = [super init];

ou quelque chose comme ça.

Je sens que je fais des choses mal ici, mais des exemples en ligne ont eu des choses similaires dans la méthode init, mais ils affectent cette sous-vue 0 à la propriété view et l'ajoutent en tant qu'enfant - mais n'est-ce pas alors un total de deux instances MyClass? Un essentiellement non lié à IBOutlets, contenant l'enfant MyClass instancié via loadNibNamed? Ou au mieux, n'est-ce pas une instance MyClass avec une UIView intermédiaire supplémentaire contenant tous les IBOutlets que je voulais à l'origine en tant qu'enfants directs de MyClass? Cela pose un léger désagrément lorsqu'il s'agit de faire des choses comme instanceOfMyClass.frame.size.width, car il renvoie 0, lorsque l'UIView enfant qui a été introduit renvoie la taille de trame réelle que je cherchais.

Est-ce que je fais mal que je joue avec loadNibNamed dans une méthode init? Dois-je faire quelque chose de plus comme ça?

MyClass *instance = [[MyClass alloc] init];
[[NSBundle mainBundle] loadNibNamed:@"MyClass" owner:instance options:nil];  

Ou comme ça?

MyClass *instance = [[[NSBundle mainBundle] loadNibNamed:@"MyClass" owner:nil options:nil] objectAtIndex:0]; 

Merci d'avance pour toute assistance.

22
Cloov

La deuxième option est la bonne. Le code le plus défensif que vous puissiez faire est le suivant:

+ (id)loadNibNamed:(NSString *)nibName ofClass:(Class)objClass {
    if (nibName && objClass) {
        NSArray *objects = [[NSBundle mainBundle] loadNibNamed:nibName 
                                                         owner:nil 
                                                       options:nil];            
        for (id currentObject in objects ){
            if ([currentObject isKindOfClass:objClass])
                return currentObject;
        }
    }

    return nil;
}

Et appelez comme ceci:

MyClass *myClassInstance = [Utility loadNibNamed:@"the_nib_name" 
                                         ofClass:[MyClass class]]; 
// In my case, the code is in a Utility class, you should 
// put it wherever it fits best

Je suppose que votre MyClass est une sous-classe de UIView? Si tel est le cas, vous devez vous assurer que le UIView de votre .xib est bien de la classe MyClass. Cela est défini sur le troisième onglet sur la partie droite dans le générateur d'interface, après avoir sélectionné la vue

23
Ismael

Il vous suffit de créer la sous-vue via loadNibNamed, de définir le cadre et de l'ajouter à la sous-vue. Par exemple, j'ajoute trois sous-vues à l'aide de ma classe MyView, qui est une sous-classe UIView dont l'interface est définie dans une NIB, MyView.xib:

Donc, je définis initWithFrame pour ma sous-classe UIView:

- (id)initWithFrame:(CGRect)frame
{
    NSLog(@"%s", __FUNCTION__);

    self = [super initWithFrame:frame];
    if (self)
    {
        NSArray *nibContents = 
          [[NSBundle mainBundle] loadNibNamed:@"MyView" 
                                        owner:self 
                                      options:nil];
        [self addSubview:nibContents[0]];
    }
    return self;
}

Ainsi, par exemple, dans mon UIViewController, je peux charger quelques-uns de ces objets sous-classés UIView comme ceci:

for (NSInteger i = 0; i < 3; i++)
{
    CGRect frame = CGRectMake(0.0, i * 100.0 + 75.0, 320.0, 100.0);
    MyView *myView = [[MyView alloc] initWithFrame:frame];
    [self.view addSubview:myView];

    // if you want, do something with it: 
    // Here I'm initializing a text field and label

    myView.textField.text = [NSString stringWithFormat:@"MyView textfield #%d",
                              i + 1];
    myView.label.text = [NSString stringWithFormat:@"MyView label #%d", 
                          i + 1];
}

J'ai conseillé à l'origine l'utilisation des contrôleurs, et je garderai cette réponse ci-dessous pour référence historique.


Réponse originale:

Je ne vois aucune référence pour afficher les contrôleurs ici. Habituellement, vous auriez une sous-classe de UIViewController, avec laquelle vous instancieriez

MyClassViewController *controller = 
  [[MyClassViewController alloc] initWithNibName:@"MyClass" 
                                          bundle:nil];

// then you can do stuff like
//
// [self presentViewController:controller animated:YES completion:nil];

Le fichier NIB, MyClass.xib, pourrait spécifier que la classe de base pour le UIView, si vous voulez, où vous avez tout le code associé à la vue (par exemple en supposant que MyClass était une sous-classe de UIView).

7
Rob

Voici une méthode que j'utilise:

  1. Créez une sous-classe pour UIView, elle sera appelée MyClass
  2. Créez un fichier de vue xib. Ouvrez dans le générateur d'interface, cliquez sur Propriétaire du fichier et dans l'inspecteur d'identité, remplacez la classe par celle de votre contrôleur de vue parent, par ex. ParentViewController.
  3. Cliquez sur la vue déjà dans la liste des objets et changez sa classe dans l'inspecteur d'identité en MyClass.
  4. Toutes les sorties/actions que vous déclarez dans MyClass seront connectées en cliquant-glissant depuis View (pas le propriétaire du fichier). Si vous souhaitez les connecter aux variables de ParentViewController, cliquez-glissez depuis le propriétaire du fichier.
  5. Maintenant, dans votre ParentViewController, vous devez déclarer une variable d'instance pour MyClass.

ParentViewController.h ajoutez ce qui suit:

@class MyClass

@interface ParentViewController : UIViewController {
    MyClass *myClass;
}

@property (strong, nonatomic) MyClass *myClass;

Synthétisez ceci dans votre implémentation et ajoutez ce qui suit dans votre méthode viewDidLoad:

- (void)viewDidLoad {
    [super viewDidLoad];

    [[NSBundle mainBundle] loadNibNamed:@"MyClass" owner:self options:nil];
    self.myClass.frame = CGRectMake(X,Y,W,H); //put your values in.
    [self.view addSubview:self.myClass];
}
2
sooper