Bindings, reordering des rows et flag de modification

AliGatorAliGator Membre, Modérateur
Bonjour à  tous !

Je suis en train de faire un petit outils sous MacOSX qui utilise quasiment uniquement les bindings. En effet c'est pour de la gestion d'un petit catalogue et sans une seule ligne de code j'ai déjà  pu faire fonctionner le tout, ça m'impressionne toujours autant ces bindings :D Du coup l'utilisateur peut naviguer dans les données via mon interface, mais aussi les modifier.

Oui mais voilà  j'ai deux petites questions :
  • y a-t-il un moyen de savoir quand mes ArrayControllers et mes bindings modifient les données, de sorte que je puisse mettre un flag shouldSave à  YES, pour ne proposer ensuite de sauver le catalogue quand on quitte que s'il a été modifié ?
  • comment peut-on (directement avec les bindings sans code ou avec code ?) permettre de réarranger les lignes d'un NSArrayController ? De sorte que l'utilisateur puisse faire passer un élément qui est en 3e position en 5e ou 1ère position par exemple, avec un simple drag & drop de la row de la tableView ?


PS : Pour info la structure de mes données est la suivante : un NSMutableArray "catalog", qui contient un tableau de NSDictionary représentant des catégories. Une catégorie = un nom + un tableau d'éléments, donc un autre NSMutableArray contenant à  son tout des NSDictionaries. Pour gérer tout ça j'utilise deux NSArrayControllers dans mon XIB, un pour le tableau de catégories (dont le contentArray est bindé à  mon catalogue) et un pour le tableau des éléments de la catégorie sélectionnée (dont le contentArray est bindé sur le model key path "selection.items" du premier ArrayController)

Réponses

  • laudemalaudema Membre
    14:52 modifié #2
    Bonjour Ali,

    dans 1284131252:

    • y a-t-il un moyen de savoir quand mes ArrayControllers et mes bindings modifient les données, de sorte que je puisse mettre un flag shouldSave à  YES, pour ne proposer ensuite de sauver le catalogue quand on quitte que s'il a été modifié ?


    1) Les controllers adoptent tous les protocoles NSEditor et NSEditorRegistration, tu devrais donc pouvoir suivre les modifications
    http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaBindings/Concepts/HowDoBindingsWork.html#//apple_ref/doc/uid/20002373-200965

    Pour le 2) je suis preneur d'une solution simple. Pour l'instant je ne connais que ce qu'Apple propose http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/DragandDrop/UsingDragAndDrop.html%23//apple_ref/doc/uid/20000726-BABFIDAB
  • AliGatorAliGator Membre, Modérateur
    14:52 modifié #3
    Merci laudema... en fait j'avais vaguement/rapidement vu NSEditor et NSEditorRegistration, mais ça veut dire que je suis obligé de sous-classer mes NSArrayControllers juste pour surcharger le "objectDidEndEditing:" pour y mettre mon flag, c'est un peu bête juste pour ça, j'aurais préféré une méthode delegate ou autre... mais bon, soit.

    Pour le Drag & Drop merci pour le lien c'est nickel je vais essayer ça... Bon ça veut donc dire qu'il faut le coder, que les NSArrayControllers ne savent à  priori pas le gérer tout seul... dommage mais bon, c'est sans doute qu'on prend vite l'habitude de ne pas écrire une seule ligne de code quand on a commencé à  utiliser les bindings que du coup on est choqué d'avoir à  en écrire 10 :D
  • laudemalaudema Membre
    14:52 modifié #4
    dans 1284328660:

    Merci laudema... en fait j'avais vaguement/rapidement vu NSEditor et NSEditorRegistration, mais ça veut dire que je suis obligé de sous-classer mes NSArrayControllers juste pour surcharger le "objectDidEndEditing:" pour y mettre mon flag, c'est un peu bête juste pour ça, j'aurais préféré une méthode delegate ou autre... mais bon, soit.

    ?
    Pour en être certain j'ai fait un projet rapidement avec un tableau qui contient un dictionnaire, mis dans IB une vue tableau avec une colonne liée à  la valeur du dico via un NSArrayController et ajouté dans le AppDelegate
    <br /><br />- (void)objectDidEndEditing:(id)editor {<br />	NSLog(@&quot;un objet %@ vient de finir d&#39;éditer&quot;, editor);<br />}<br />
    

    Une fois lancée l'appli si je clique dans la vue tableau et que je change la valeur j'obtiens bien

    un objet <NSArrayController: 0x1004113d0>[object class: NSMutableDictionary, number of selected objects: 1] vient de finir d'éditer

    Pas de besoin de sous-classer ??
  • CéroceCéroce Membre, Modérateur
    14:52 modifié #5
    dans 1284131252:

    y a-t-il un moyen de savoir quand mes ArrayControllers et mes bindings modifient les données, de sorte que je puisse mettre un flag shouldSave à  YES, pour ne proposer ensuite de sauver le catalogue quand on quitte que s'il a été modifié ?

    Pour ce genre de choses, la méthode classique est quand même d'utiliser l'undo manager.
    Je sais bien que tu bidouilles un truc vite fait, mais on en revient toujours au même principe: Cocoa est facile à  utiliser tant que l'on fait des choses standard.
  • AliGatorAliGator Membre, Modérateur
    14:52 modifié #6
    laudema : étonnant ce que tu décris, vu que d'après la doc, les protocoles informels NSEditor et NSEditorRegistration sont adoptés/implémentés par la classe NSController dont dérive NSArrayController & co...
    Donc d'après la doc c'est NSArrayController qui implémente objectDidEndEditing (et qu'il faudrait donc sous-classer pour implémenter son propre comportement là -dedans), c'est pas un message envoyé à  un delegate du NSArrayController et encore moins au AppController (qu'est ce qu'il vient faire là -dedans, le delegate de l'application, qui n'est à  priori pas le delegate du NSArrayController ?)
    J'ai dû louper un truc dans la doc moi... car ton test marche d'après ce que tu dis et tant mieux ça m'arrange... mais j'aimerais bien comprendre pourquoi :P
  • laudemalaudema Membre
    14:52 modifié #7
    dans 1284378930:

    ton test marche d'après ce que tu dis et tant mieux ça m'arrange... mais j'aimerais bien comprendre pourquoi :P

    Si tu veux le test je te l'envoie :-)
    Si tu double-cliques sur objectDidEndEditing: en tenant la touche commande enfoncée tu peux remonter jusqu'à  la définition de la méthode. Sauf dans le cas où elle est définie dans plusieurs endroits auquel cas tu as un pop-up pour choisir.
    objectDidEndEditing est donc définie pour NSObject via NSKeyValueBinding.h en plus des NSObjectController
    <br /><br />// methods implemented by controllers, CoreData&#39;s managed object contexts (and potentially documents)<br />@interface NSObject (NSEditorRegistration)<br /><br />- (void)objectDidBeginEditing:(id)editor;<br />- (void)objectDidEndEditing:(id)editor;<br /><br />@end<br />
    

    Etant définie elle est appelée, et il me semble que c'était voulu au départ par Apple..
  • laudemalaudema Membre
    14:52 modifié #8
    dans 1284387748:

    dans 1284378930:

    ton test marche d'après ce que tu dis et tant mieux ça m'arrange... mais j'aimerais bien comprendre pourquoi :P

    Si tu veux le test je te l'envoie :-)
    Si tu double-cliques sur objectDidEndEditing: en tenant la touche commande enfoncée tu peux remonter jusqu'à  la définition de la méthode. Sauf dans le cas où elle est définie dans plusieurs endroits auquel cas tu as un pop-up pour choisir.
    objectDidEndEditing est donc définie pour NSObject via NSKeyValueBinding.h en plus des NSObjectController pour permettre aux vues d'utiliser les bindings. Si j'ai bien compris le dernier chapitre de "Les Design Patterns de Cocoa"..
    <br /><br />// methods implemented by controllers, CoreData&#39;s managed object contexts (and potentially documents)<br />@interface NSObject (NSEditorRegistration)<br /><br />- (void)objectDidBeginEditing:(id)editor;<br />- (void)objectDidEndEditing:(id)editor;<br /><br />@end<br />
    

  • AliGatorAliGator Membre, Modérateur
    14:52 modifié #9
    dans 1284387748:

    dans 1284378930:

    ton test marche d'après ce que tu dis et tant mieux ça m'arrange... mais j'aimerais bien comprendre pourquoi :P

    Si tu veux le test je te l'envoie :-)
    Si tu double-cliques sur objectDidEndEditing: en tenant la touche commande enfoncée tu peux remonter jusqu'à  la définition de la méthode. Sauf dans le cas où elle est définie dans plusieurs endroits auquel cas tu as un pop-up pour choisir.
    objectDidEndEditing est donc définie pour NSObject via NSKeyValueBinding.h en plus des NSObjectController
    <br /><br />// methods implemented by controllers, CoreData&#39;s managed object contexts (and potentially documents)<br />@interface NSObject (NSEditorRegistration)<br /><br />- (void)objectDidBeginEditing:(id)editor;<br />- (void)objectDidEndEditing:(id)editor;<br /><br />@end<br />
    

    Etant définie elle est appelée, et il me semble que c'était voulu au départ par Apple..
    Oui je connais le Pomme-Double-Clic et ce que tu décris est parfaitement normal, y compris la déclaration dans NSObject : en effet NSEditorRegistration est un protocole informel, donc comme tout protocole informel il est déclaré comme une catégorie de NSObject, pour que si la doc dit qu'un objet A est le delegate d'un objet B et que ce delegate doit se conformder à  ce protocole informel NSEditorRegistration, alors il doit implémenter ces méthodes.
    Cela veut dire que n'importe quel NSObject a le droit d'être un delegate d'un objet B donné. Ce n'est pas pour autant que les méthodes sont appellées dans tout et n'importe quoi et dans n'importe quel type d'objet : ce n'est pas parce que la méthode est déclarée qu'elle est appellée sur tous les NSObjects par tous les NSControllers.

    (Exemple type, pour NSURLConnection, -- qui est encore une des rares classes usuelles à  utiliser des protocoles informels depuis que les protocoles formels ont la possibilité de déclarer les méthodes @optional et que tout est maintenant migré du coup vers les protocoles formels -- tu as les méthodes [tt]connection:didFailWithError:[/tt] ou [tt]connectionDidFinishLoading:[/tt] qui sont des méthodes de delegate mais déclarées comme un protocole informel, donc comme catégorie de NSObject. Mais c'est pas pour autant que ces méthodes sont appellées sur tous les objets dans lesquels tu les déclares, et que par exemple si tu implémentes ces méthodes dans ton AppDelegate elles seront appellées par tous les NSURLConnection que tu vas utiliser dans ton appli ! (et encore heureux que ça ne se passe pas comme ça !). Elles ne sont appellées que sur l'objet qui est déclaré comme "delegate" de ton NSURLConnection, ce delegate pouvant être n'importe quel NSObject mais encore faut-il indiquer à  NSURLConnection quel est cet objet qui servira de delegate.

    Or pour NS(Array)Controller on ne le fait pas, il n'y a pas de notion de delegate d'indiquée dans la doc nous permettant d'indiquer l'objet qui servira de delegate (qui pourrait si on le choisissait être notre AppDelegate, soit l'objet servant de delegate également à  ta NSApplication, mais là  on peut même pas le choisir). Au contraire l'application dit que ce protocole informel NSEditorRegistration est implémenté par NSController, ce qui veut dire que c'est NSController lui-même qui est conforme au protocole NSEditorRegistration, c'est lui qui implémente les méthodes [tt]objectDidEndEditing:[/tt] & co, c'est pas un autre objet (comme par exemple ton AppDelegate) qui sert de delegate et qui se devrait pour cela d'être conforme au protocle NSEditorRegistration...

    Donc il manque toujours un maillon dans l'explication, c'est pas cohérent en tout cas avec la doc....
    je testerai ce soir en faisant comme toi, en implémentant les méthodes dans mon AppDelegate et en mettant un breakpoint dedans pour comprendre la callstack, mais même si ça marche je persiste à  dire que c'est louche, soit ils ont pas tout dit dans la doc (le plus probable à  mon avis vu ce que tu décris) soit c'est un effet de bord bizarre...
  • laudemalaudema Membre
    septembre 2010 modifié #10
    D'après "Les Design Patterns de Cocoa" c'est implémenté pour que les vues puissent l'utiliser donc, pour moi, il me semble que ça fait partie de AppKit. Un peu comme les NSAttributedString qui se mettent à  des choses en plus ou comme observeValueForKeyPath: qui s'auto-complete dans n'importe quel projet AppKit (pour rester dans le domaine des bindings par définition utiles aux vues)
    En tout cas c'est comme ça que je le vis mais je ne demande qu'à  être éclairé :-)
  • laudemalaudema Membre
    14:52 modifié #11
    Tu parlais de la pile, on voit bien que ça vient de la vue tableau mais pour être récupéré par le ArrayController ensuite via [_NSBindingAdaptor _editorDidEndEditing] qui doit contribuer à  la "magie" ?
  • AliGatorAliGator Membre, Modérateur
    septembre 2010 modifié #12
    Bon alors en fait le message est envoyé à  l'objet bindé au "contentArray", dans ton cas c'était le AppDelegate, dans mon cas c'est mon objet qui me sert de contrôlleur de mon application et s'interface avec le modèle
    Bon bah c'est donc qu'ils avaient oublié de précisé que la notification était aussi envoyée à  l'objet bindé au NSArrayController alors.

    J'attaque le reordering des rows (pour l'instant j'ai suivi la doc Apple, ça marche pas j'ai beau cliquer sur une ligne et essayer de la changer de place il ne fait que sélectionner la suivante... mais bon je creuse j'ai dû zapper un truc)
    [EDIT] C'est bon ça marche (l'erreur conne, j'avais oublié de relier un IBOutlet) pour le reordering aussi !

    Merci bien en tout cas maintenant tout marche nickel. Un peu "déçu" que le reordering soit pas automatique avec les bindings, mais bon :P
  • laudemalaudema Membre
    septembre 2010 modifié #13
    dans 1284401085:

    Un peu "déçu" que le reordering soit pas automatique avec les bindings, mais bon :P

    Probablement pas autant que moi quand j'en ai eu besoin !
    Je suis très content d'avoir pu t'aider !
    ça s'arrose  <3 tournée générale  :p (ça devrait aller il ne semble pas passer trop de monde par ici ;) :p
  • laudemalaudema Membre
    14:52 modifié #14
    Bonjour,

    Dur dur le réveil, le Picon bière c'est plutôt traitre comme breuvage, Perrier pour le restant de la journée ;-)
    Mais la nuit portant quand même conseil, ce matin en me remettant dans ma machine je me suis souvenu d'un projet exemple d'Apple pour le drag 'n drop qui s'appelait Bookmarks (la mémoire humaine est supérieure à  l'informatique en ce sens qu'elle fonctionne même quand le système est au repos ;).
    Par contre pas moyen de remettre la main dessus depuis la documentation Xcode, en fait c'est un projet récupéré depuis le site http://homepage.mac.com/mmalc/CocoaExamples/controllers.html il y a juste à  changer le SDK d'origine (10.4) pour le faire tourner.
    Il n'est pas impossible que ma mémoire ait défailli parce qu'il utilise une sous-classe de NSArrayController et que j'ai souvent lu qu'il ne fallait pas faire ça. Pourtant le copyright des fichiers semble être d'Apple ...
    Dommage parce que du coup tu n'aurais pas eu de difficulté pour implémenter en plus didEndEditing ..

    Je le mets en pièce jointe, au cas où il intéresserait quelqu'un d'autre, avec un DNDArrayController il n'y a besoin de rien s'occuper pour ajouter le Drag 'N Drop à  une vueTableau ..
  • AliGatorAliGator Membre, Modérateur
    14:52 modifié #15
    Bah en fait merci bien mais j'étais également allé sur le site mmalc, et avait trouvé en effet cet exemple Bookmarks (que je n'ai même pas compilé j'ai juste regardé le fichier intéressant via QuickLook et m'en suis inspiré) en tout cas maintenant ça marche nickel (en fait fallait juste savoir que ce reordering ne pouvait pas être fait automatiquement par les bindings et qu'il fallait faire le code, après le code en lui-même étant dans la doc Apple c'est pas bien dûr à  implémenter)

    Mais merci quand même ça pourra servir pour d'autres de toute façon :)
Connectez-vous ou Inscrivez-vous pour répondre.