Quel pattern pour un view controller avec un mode édition

Bonjour,


 


Je tourne un peu en rond car je n'arrive pas à  trouver le meilleur pattern pour faire un view controller qui propose un mode édition et qui peut être affiché modalement ou via un push de navigation controller. Typiquement si on prend le fiche contact de l'application Contacts :


 - quel viewcontroller serait responsable de créer un context spécifique pour l'édition, le view controller Contact ou le view controller qui le crée ?


 - quel viewcontroller serait responsable de sauver ou libérer le context pour l'édition ? Le même que la question ci-dessus j'imagine.


 - quel viewcontroller serait responsable de configurer les boutons dans la barre de navigation ?


 - du coup quel serait l'interface publique du view controller Contact ?


 


J'espère que ma question n'est pas trop floue.


Je vous remercie d'avance,


Quentin


Réponses

  • CéroceCéroce Membre, Modérateur
    juin 2014 modifié #2
    Ayant été confronté à  cette problématique récemment, je m'étais également posé la question, que je vais reformuler: faut-il deux classes de View Controllers, une pour l'édition, et une pour la consultation, ou une seule avec le choix d'un mode "édition" ou "consultation".

    Je crois que ça dépend de l'application. Dans mon cas précis, j'ai rapidement exclu d'utiliser plusieurs classes parce que je n'avais pas deux modes, mais quatre. Je n'allais clairement pas créer 4 classes qui auraient eu beaucoup de code redondant, que j'aurais certes pu factoriser.
    La solution pour moi fut de transférer une grosse partie de la logique dans les cellules, auxquelles j'ai ajouté une propriété "éditable" et qui changeaient leur apparence selon.

    Si tu n'as que deux modes, je pense que cette solution se discute beaucoup... avec deux classes, le code est plus simple.

    Pour ce qui est de la gestion de la barre de navigation, etc., c'est du ressort du view controller de consultation/édition. C'est lui qui sait le mieux quels boutons doivent s'afficher et dans quel état. Il communiquera son résultat à  son view controller parent par l'habituelle délégation.
  • AliGatorAliGator Membre, Modérateur
    Pour ce qui est du MOC CoreData pour moi c'est clairement le VC d'édition (et pas le VC parent qui le présente en modale) qui doit posséder la propriété pour stocker le MOC fils et c'est aussi lui qui doit faire le save sur le parent.


    La raison en est simple : Principe d'encapsulation et réduire les interdépendances des classes.
  • Merci pour vos retours. Du coup, suite à  un petit refactoring j'arrive à  une classe QAContactViewController qui fonctionne selon 3 modes : consultation, édition et création. Ces modes sont définis selon l'appel à  une des trois méthodes ci-dessous :


     

    - (void)createContextFromContext:(NSManagedObjectContext *)fromContext;

     

    - (void)editContactID:(NSManagedObjectID *)contactID fromContext:(NSManagedObjectContext *)fromContext;

     

    - (void)showContactID:(NSManagedObjectID *)contactID fromContext:(NSManagedObjectContext *)fromContext;

     

    Maintenant la classe QAContactViewController gère son propre managed object context privé pour l'édition et la création.

     

    Ca a l'air de pas mal fonctionner.

     

    Quentin

  • AliGatorAliGator Membre, Modérateur
    Pourquoi passer un NSManagedObjectID + un MOC plutôt que de passer le NSManagedObject directement (et de récupérer son MOC associé

    via la propriété "managedObjectContext" du NSManagedObject) ?


    D'autant qu'en faisant ainsi ton API n'est pas claire, tu ne dis pas à  quelle entité doit correspondre ton ID, et en + tu peux finir par passer n'importe quoi en paramètre (genre des choses pas consistantes entre elles)
  • AliGatorAliGator Membre, Modérateur
    En gros moi ce que je ferais :

    -(void)showContact:(QAContact*)contact
    {
    // Ici on utilise directement le contact, dans le contexte dans lequel il a été passé
    // En effet, dans le mode "show" il n'y a aucune raison de créer un MOC fils, on peut
    // utiliser le même MOC que celui qui est utilisé par le VC parent
    self.contact = contact;
    ...
    // et on utilise self.contact pour remplir notre UI
    }

    -(void)editContact:(QAContact*)contact
    {
    // Ici par contre on va créer un MOC fils, pour pouvoir faire toutes les opérations que
    // l'on veut sur le contact dans un MOC enfant (comme une branche en GIT si tu veux)

    // On récupère le MOC auquel le contact passé en paramètre est associé
    NSManagedObjectContext* parentCtx = contact.managedObjectContext;
    // On crée un MOC fils de ce MOC d'origine
    NSManagedObjectContext* childCtx = [NSManagedObjectContext MR_contextWithParent:parentCtx];
    // On va ensuite manipuler le NSManagedObject contact dans ce MOC fils et non dans son MOC d'origine
    self.contact = [contact MR_inContext:childCtx];
    ...
    // Et on utilise self.contact pour remplir notre UI, et de même quand on change la valeur
    // d'un champ de texte dans l'UI on modifie les propriétés de self.contact, ce qui ne va appliquer
    // ces changements que dans le childCtx sans impacter le parentCtx (comme dans une branche GIT)
    }

    -(IBAction)saveEdits
    {
    // Méthode appelée quand l'utilisateur tap sur le bouton "OK"/"Save" pour valider ses changements
    // N'est donc utilisée que pour le mode édition

    // On va sauver le contexte fils, de sorte que les changements soient répercutés sur le MOC parent
    // (Sur le même principe d'un merge GIT d'une branche sur sa branche parente, si tu veux)
    [self.contact.managedObjectContext save];

    [self.presentingViewController dismissViewControllerAnimated:YES];
    }
    -(IBAction)cancelEdits
    {
    // Il n'y a rien à  faire : il suffit juste de ne pas sauver le childContext sur le parent
    // (et de laisser ce child MOC se faire dealloc par ARC et tomber dans l'oubli)

    [self.presentingViewController dismissViewControllerAnimated:YES];
    }
    Enfin c'est l'idée générale en tout cas que je mettrais en place pour ma part.

    Comme ça dans ton ViewController parent, tu n'as pas besoin de te poser la question de quel contexte je dois passer, savoir à  quoi correspond le NSManagedObjectID que je dois passer, etc... Tu n'as pas non plus à  créer le contexte fils dans le VC appelant/parent. Non, rien de tout ça.
    Si ton VC parent par exemple c'est un UITableViewController qui liste tes contacts, bah dans le "tableView:didSelectRowAtIndexPath:" tu récupères le contact de la même manière que tu le récupères dans ton "tableView:cellForRowAtIndexPath:" pour remplir ta cellule... et tu passes ce contact (NSManagedObject) directement au EditViewController. Sans autre forme de procès (sans avoir à  le convertir en NSManagedObjectID d'un côté, à  passer un MOC de l'autre en 2ème paramètre, ni à  créer un contexte fils toi-même ni quoi que ce soit).

    Tu récupères le NSManagedObject* contact de ton NSArray ou autre qui te sert de dataSource (ou de ton NSFetchedResultsController par exemple) comme tu le fais pour "cellForRowAtIndexPath:", tu instancies ton EditViewController, et tu appelles "editContact:" dessus en lui passant ce contact en paramètre. Point barre.
Connectez-vous ou Inscrivez-vous pour répondre.