iPhone SDK: quelle est la différence entre loadView et viewDidLoad?

136

Lorsque vous travaillez avec des vues et des contrôleurs de vue dans une application iPhone, quelqu'un peut-il expliquer la différence entre loadView et viewDidLoad?

Mon contexte personnel, c'est que je construis toutes mes vues à partir du code, je n'utilise pas et n'utiliserai pas Interface Builder, si cela fait une différence.

J'ai constaté que souvent, lorsque j'ajoute du code d'initialisation à loadView, je me retrouve avec une trace de pile infinie, donc je fais généralement toute ma construction de vue enfant dans viewDidLoad ... mais je ne sais vraiment pas quand chacun est exécuté, et quel est l'endroit le plus approprié pour mettre le code d'initialisation. Ce qui serait parfait, c'est un simple diagramme des appels d'initialisation.

Merci!

ryan.scott
la source

Réponses:

200

Je peux deviner quel pourrait être le problème ici, car je l'ai fait:

J'ai constaté que souvent, lorsque j'ajoute du code d'initialisation à loadView, je me retrouve avec une trace de pile infinie

Ne lisez pas self.view dans -loadView. Seulement mettre , ne pas obtenir ce.

L'accesseur de propriété self.view appelle -loadView si la vue n'est pas actuellement chargée. Voilà votre récursivité infinie.

La façon habituelle de créer la vue par programme dans -loadView, comme illustré dans les exemples pré-Interface-Builder d'Apple, est plus comme ceci:

UIView *view = [[UIView alloc] init...];
...
[view addSubview:whatever];
[view addSubview:whatever2];
...
self.view = view;
[view release];

Et je ne vous blâme pas de ne pas utiliser IB. Je suis resté fidèle à cette méthode pour tout Instapaper et je me sens beaucoup plus à l'aise avec elle que de gérer les complexités d'IB, les bizarreries d'interface et le comportement inattendu en coulisses.

Marco
la source
ahhhh, merci pour une explication, enfin! J'ai évité l'idiome d'allouer une variable temporaire, puis de définir self.view, puis de relâcher ... cela semblait quelque peu maladroit, inutile. Je peux maintenant comprendre pourquoi cette décision m'aurait conduit sur le chemin où je me trouve maintenant.
ryan.scott
J'ai un tel code et il n'y a pas de récursivité. Pourquoi? -(void) loadView { // Frame for Hypnosis view CGRect frame = [[UIScreen mainScreen] bounds]; // Create a Hipnosis view v = [[HypnosisView alloc] initWithFrame:frame]; self.view = v;
user2054339
44

loadViewest la méthode UIViewControllerqui chargera réellement la vue et l'affectera à la viewpropriété. C'est également l'emplacement qu'une sous-classe de UIViewControllerremplacerait si vous vouliez configurer la viewpropriété par programme.

viewDidLoadest la méthode qui est appelée une fois la vue chargée. Ceci est appelé après l'appel de loadView. C'est un endroit où vous pouvez remplacer et insérer du code qui effectue la configuration initiale supplémentaire de la vue une fois qu'elle a été chargée.

NilObject
la source
14
viewDidLoad()

doit être utilisé lorsque vous chargez votre vue à partir d'un NIB et que vous souhaitez effectuer une personnalisation après le lancement

LoadView()

doit être utilisé lorsque vous souhaitez créer votre vue par programmation (sans utiliser Interface Builder)

Ashokdy
la source
Cela peut avoir un problème, j'ai un test lorsque mon contrôleur de vue n'était pas associé au fichier NIB, viewDidLoad toujours appelé
ruandao
11

Il suffit d'ajouter quelques exemples de code pour montrer ce que NilObject a dit:

- (void)loadView
{
    // create and configure the table view
    myTableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStyleGrouped];   
    myTableView.delegate = self;
    myTableView.dataSource = self;
    myTableView.scrollEnabled = NO;
    self.view = myTableView;

    self.view.autoresizesSubviews = YES;
}

- (void)viewDidLoad 
{
  self.title = @"Create group";

  // Right menu bar button is to Save
  UIBarButtonItem *saveButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(save)];
  self.navigationItem.rightBarButtonItem = saveButtonItem;
  [saveButtonItem release];
}
alamodey
la source
4
Alors, entre vous deux, est-il exact de dire que loadView est l'endroit où je dois faire l'allocation / init de self.view de mon contrôleur, et que les vues enfants doivent être gérées dans viewDidLoad (ou version ultérieure)?
ryan.scott le
2

Pour éviter qu'une boucle infinie ne se produise lorsque vous lisez self.view, appelez la super implémentation de la classe lorsque vous chargez une vue. La super implémentation vous attribuera un nouvel UIView.

- (void) loadView {
[super loadview];

// init code here...

[self.view addSubView:mySubview1]; //etc..

}
futureelite7
la source
6
Je pourrais jurer que la documentation d'Apple disait que vous ne devriez pas appeler [super loadView];. Cela a été contredit dans les exemples, mais je pense que la documentation l'a correctement dit (j'ai trouvé de nombreux bogues dans les exemples au fil du temps). [super loadView]est cependant nécessaire pour UITableViewController, etc. Toutefois! Toute configuration après chargement (par exemple, l'ajout de sous-vues supplémentaires) doit être effectuée dans viewDidLoad.
Ivan Vučica
J'ai appelé [super loadView] sans aucun effet secondaire jusqu'à présent. Cela peut être vrai si vous avez l'intention de définir votre propre vision de quelque chose que vous avez fait vous-même.
futureelite7
Si vous appelez [super loadView] dans loadView, il tentera de charger la vue depuis une pointe si elle est disponible avec le nom par défaut. Vous devez donc être prudent.
Ian1971
Et si vous appelez [super loadView], vous initialisez self.view dans la méthode super loadView
Alex Nazarsky
1

Le moyen le plus simple d'utiliser loadView est de créer un type de contrôleur de vue de base, comme MyBaseViewController qui est une sous-classe de UIViewController. Dans sa méthode loadView, créez une vue de cette manière:

-(void) loadView {
    if ([self viewFromNib]) {
        self.view = [self viewFromNib];
    } else {
        self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    }
    self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight;
    self.view.backgroundColor = [UIColor whiteColor];
}

Et lorsque vous avez besoin de créer un contrôleur de vue, vous utilisez simplement la sous-classe de MyBaseViewController et dans son contrôleur loadView, vous appelez simplement [super loadView] comme ceci

//sucblass loadView
-(void) loadView {
    [super loadView];

    //rest of code like this..
    UILabel *myLabel = [[UILabel alloc] initWithFrame:myFrame];
    [self.view addSubview:myLabel];
    [myLabel release];
}
Josip B.
la source
1

loadView()est appelé lorsque votre contrôleur est invité à créer son fichier self.view. Vous pouvez le faire par vous-même comme

self.view = [UIView alloc] init...];

Ou la classe UIController parent de votre contrôleur a déjà un nom de méthode -loadView()qui initialise votre self.view en vue vide. Ensuite, vous pouvez appeler

[super loadView];

Je recommande vraiment la deuxième approche car elle encourage l'héritage. Uniquement si votre contrôleur de vue n'est pas directement hérité de UIViewController.

Dulguun Otgon
la source
0

La définition donnée par Apple sur viewDidLoad mentionne qu'elle est appelée après le chargement de la vue du contrôleur en mémoire. Pour le mettre en un terme simple, c'est la première méthode qui se chargera.

Vous vous demandez peut-être dans quelles conditions cette méthode sera-t-elle pleinement utilisée? La réponse est, en gros, tout ce que vous vouliez que l'application se charge en premier. Par exemple, vous voudrez peut-être une couleur d'arrière-plan différente, au lieu du blanc, vous pouvez peut-être choisir le bleu.

Gulsan Borbhuiya
la source