web-dev-qa-db-fra.com

iOS 7 - Différence entre viewDidLoad et viewDidAppear

Désolé, mais cela peut ne pas être une question de programmation en soi, mais plutôt une question sur la nature des fonctions du cycle de vie iOS. 

J'ai une application dans laquelle j'ai une fonction qui crée quatre tableaux et les renseigne via des requêtes de base de données. Au début, j’ai appelé la fonction à partir de la fonction viewDidLoad; toutefois, chaque fois que la vue est chargée, il faut un certain temps (environ 3 à 4 secondes) pour que la vue apparaisse réellement. J'ai donc créé une variable activityViewIndicator et ma fonction viewDidLoad ressemble à ceci:

- (void)viewDidLoad:(BOOL)animated{
    [super viewDidLoad];

    NSLog(@"viewDidLoad Entered");
    [self.activityIndicatorView startAnimating];

    partInput.delegate = self;
    brandInput.delegate = self;
    barcodeInput.delegate = self;
    itemNameInput.delegate = self;

    //initializeArrays is the function that initializes the arrays
    [self initializeArrays];

    [self.activityIndicatorView stopAnimating];

}

Toutefois, cela ne fonctionne pas car la fonction viewDidLoad est déclenchée lorsque l'application se trouve toujours dans la vue précédente. La vue ne s'affiche que lorsque viewDidLoad est déjà terminé. J'ai donc plutôt déplacé l'initialisation du tableau vers ma fonction viewDidAppear qui ressemble à ceci: 

- (void)viewDidAppear:(BOOL)animated{
    NSLog(@"viewDidAppear loaded successfully");
    [self.activityIndicatorView startAnimating];

    partInput.delegate = self;
    brandInput.delegate = self;
    barcodeInput.delegate = self;
    itemNameInput.delegate = self;

    [self initializeArrays];

    [self.activityIndicatorView stopAnimating];

}

Cependant, lorsque j'ai déployé cette solution, aucun retard n'a été constaté, ce qui rendait ActivityIndicatorView inutile. 

Ma question est la suivante: pourquoi me semble-t-il qu'il existe une "différence de performance" entre viewDidLoad et viewDidAppear

40
Razgriz

Veuillez suivre les étapes ci-dessous. Afficher le cycle de vie du contrôleur à chaque fois. Vous serez surpris par le codage et les performances de votre application de cette manière.

enter image description here

223
user2223516

Je vais vous renvoyer aux documents d'Apple, car je pense qu'il est nécessaire d'expliquer davantage le cycle de vie du View Controller au-delà de la simple réponse à votre question.

https://developer.Apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html

En fin de compte, votre contrôleur de vue a un cycle de vie:

init - cependant, vous initialisez votre contrôleur de vue

viewWillLoad/viewDidLoad - appelé lors de la construction de la vue (via le premier appel permettant de récupérer l'UIView du contrôleur de la vue via sa propriété view - également appelé chargement paresseux)

viewWillAppear: - lorsque la vue est prête à apparaître immédiatement (animée == NON) ou à afficher une transition (animée == OUI)

viewDidAppear: - si l'apparence de la vue n'a pas été annulée et que la vue du contrôleur de la vue apparaît complètement

viewWillDisappear: - complète viewWillAppear:

viewDidDisappear: - complète viewDidAppear:

viewWillUnload/viewDidUnload - API obsolètes lorsque la vue est déchargée en raison de contraintes de mémoire (ne vous en préoccupez plus)

dealloc - le contrôleur de vue lui-même est désalloué

En fin de compte cependant, je pense que votre problème peut être que vous bloquez le thread principal avec l'initialisation de votre tableau. Vous devriez lire sur la programmation asynchrone mais en attendant, vous pourriez faire quelque chose comme ceci:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // other stuff

    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf) {
            [strongSelf initializeArraysSynchronously];
            dispatch_async(dispatch_get_main_queue(), ^{
                strongSelf.doneIntializingArrays = YES;
                [strongSelf.activityIndicatorView stopAnimating];
            });
        }
    });
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    if (!self.doneInitializingArrays) {
        [self.activityIndicatorView startAnimating];
    } 
}
4
NSProgrammer

Il n'y a absolument aucune différence de performance entre viewDidLoad: et viewDidAppear :. Les deux sont des fonctions normales fonctionnant sur le thread principal. Si le chargement de votre méthode initializeArrays nécessite 3 secondes, cela prendra 3 secondes, quelle que soit la méthode que vous appelez. Puisque vous ne modifiez pas explicitement les threads, toute fonction dans laquelle vous appelez initializeArrays ne se fermera pas avant la fin.

L’appel de [self.activityIndicatorView startAnimating] "marquera" en gros l’activité activityIndicatorView afin qu’une autre fonction de l’interface utilisateur sur le fil principal le lance en animation. (C'est pourquoi le fil principal ou 'UI' est important, car toutes les animations et mises à jour visuelles de l'écran sont coordonnées). Ainsi, la fonction qui activera effectivement activityIndicator ne sera appelée qu'une fois que initializeArrays sera terminé et que vous aurez déjà appelé "stopAnimating".

Essaye ça:

- (void)viewDidLoad:(BOOL)animated{
    [super viewDidLoad];

    NSLog(@"viewDidLoad Entered");
    [self.activityIndicatorView startAnimating];

    partInput.delegate = self;
    brandInput.delegate = self;
    barcodeInput.delegate = self;
    itemNameInput.delegate = self;
}

- (void)viewDidAppear:(BOOL)animated{
    //initializeArrays is the function that initializes the arrays
    [self initializeArrays];
    [self.activityIndicatorView stopAnimating];
}
2
devdavid

Cependant, lorsque vous chargez des éléments à partir d'un serveur (ou d'un traitement de données volumineux), vous devez également prendre en compte la latence. Si vous intégrez toutes vos communications réseau dans viewDidLoad ou viewWillAppear, elles seront exécutées avant que l'utilisateur ne puisse voir la vue, ce qui peut entraîner un bref freeze de votre application. Il peut être judicieux de montrer d’abord à l’utilisateur une vue non peuplée avec un indicateur d’activité. Lorsque vous avez terminé votre mise en réseau, ce qui peut prendre une seconde ou deux (ou même échouer - qui sait?), Vous pouvez remplir la vue avec vos données. De bons exemples sur la façon dont cela pourrait être fait peuvent être vus dans divers clients Twitter. Par exemple, lorsque vous affichez la page de détail de l'auteur dans Twitterrific, la vue indique uniquement "Chargement en cours ..." jusqu'à ce que les requêtes réseau soient terminées.

ViewDidLoad appelle une seule fois lorsque vous avez initialisé votre ViewController mais Viewdidapper appelle à chaque fois.

1
Pandey_Laxman

La activityIndicatorViews n'animera que si le fil principal (le fil d'interface utilisateur) n'est pas occupé. viewDidLoad: et viewDidAppear: sont tous deux exécutés sur le thread principal. Si, comme vous l'avez dit, la méthode initializeArrays ne s'exécute pas dans un thread séparé, la activityIndicatorViews n'aura jamais le temps de s'animer.

1
ThomasW

View Did Load - Première méthode, appelée lorsque la vue est chargée pour la première fois mais n'apparaît pas sur l'écran/la fenêtre, mais uniquement chargée.

appelé seulement une fois lorsque la vue est chargée pour la première fois. 

La vue est apparue - Après que viewWillAppear a été appelé, viewDidAppear sera appelé. Cela signifie que la vue est maintenant affichée à l'écran. 

Appelé nombre de fois où l'utilisateur passe de ce contrôleur de vue à un autre contrôleur de vue et revient.

**

  • Voir le cycle de vie

**

1) ViewDidLoad (appelé uniquement lorsque view est chargé pour la première fois), puis 2) ViewWillAppear (appelé le nombre de fois), puis 3) ViewDidAppear (appelé le nombre de fois), puis 4) ViewWillDisAppear (sera appelé le nombre de fois), puis 5) ViewDidDisAppear (sera appelé le nombre de fois) 

1
Anubhav Giri

Pour ceux qui codent par programmation (sans Interface Builder), loadView est la première méthode de cycle de vie disponible, et non viewDidLoad, et le développement programmatique utilise souvent davantage loadView que viewDidLoad, gardez cela à l'esprit. Une partie de ce que fait IB consiste à écrire la loadView pour vous. IB n’est qu’un moyen de raccourcir le développement programmatique, mais si vous souhaitez mieux comprendre Cocoa Touch, vous devez le comprendre par programmation.

loadView vient en premier et est l'endroit où les éléments de l'interface utilisateur sont généralement créés, y compris la vue du contrôleur de vue lui-même (que le développement par programme doit créer explicitement). Des contraintes peuvent être ajoutées ici, mais elles ne sont pas gérées avant la fin du cycle de vie.

viewDidLoad est l'endroit où la "logique" est généralement créée une fois que les éléments de l'interface utilisateur ont été établis.

viewWillAppear et viewWillLayoutSubviews sont appelés juste avant que l'interface utilisateur elle-même ne se construise.

viewDidLayoutSubviews est appelé next et peut être appelé plusieurs fois sur le même contrôleur de vue avant qu'il n'apparaisse. C'est ici que la mise en page automatique est appliquée. C'est également à cet endroit que le programmeur peut obtenir les valeurs de la zone sécurisée d'une vue car elles ne sont pas disponibles avant cette méthode.

viewDidAppear arrive en dernier après l'affichage de la vue du contrôleur de vue dans une hiérarchie de vues.

0
bsod