[Résolu] binding entre NSArrayController et NSView perso bi-directionnel

pixmanpixman Membre
août 2010 modifié dans Objective-C, Swift, C, C++ #1
Bonjour,
je suis en train de coder une vue personnalisé afin de récupérer le chemin du fichier qu'on vient de glisser dessus et d'en afficher un aperçu.

Cette vue doit être liée à  un NSArrayController afin de dialoguer avec mon managed objectModel.

Actuellement mon binding est unidirectionnel, si une modification est faite sur les données gérées par mon NSArrayCOntroller, ma vue est bien informée. mais dans l'autre sens ça ne marche pas.

Dans ma vue personalisées j'ai ajouter les lignes suivantes pour informer du binding :
+ (void)initialize<br />{<br />&nbsp;  [self exposeBinding:@&quot;pathToPicture&quot;];<br />}<br /><br /><br />-(NSString *)pathToPicture{<br />&nbsp;  NSLog(@&quot;pathToPicture %@&quot;,pathToPicture);<br />&nbsp;  return pathToPicture;<br />}<br /><br />-(void)setPathToPicture:(NSString *)newPathToPicture{<br />&nbsp;  <br />&nbsp;  [self willChangeValueForKey:@&quot;pathToPicture&quot;];<br />&nbsp;  [pathToPicture release];<br />&nbsp;  pathToPicture = [newPathToPicture copy];<br />&nbsp;  [self didChangeValueForKey:@&quot;pathToPicture&quot;];<br />&nbsp;  NSLog(@&quot;setPathToPicture %@&quot;,pathToPicture);<br />}


dans mon code principal je réalise le "lien" de cette façon :
<br />- (void)windowControllerDidLoadNib:(NSWindowController *) windowController <br />{<br />&nbsp; &nbsp; [super windowControllerDidLoadNib:windowController];<br />&nbsp;  [vuePerso bind:@&quot;pathToPicture&quot; toObject:monArrayController withKeyPath:@&quot;selection.cheminPhoto&quot; options:nil];<br /> <br />}<br />


au final vuePerso est bien informé des changements, mais pas l'inverse !

j'ai donc tenter d'ajouter ceci dans mon "windowControllerDidLoadNib"
<br />[monArrayController bind:@&quot;selection.cheminPhoto&quot; toObject:vuePerso withKeyPath:@&quot;pathToPicture&quot; options:nil];<br />


mais cela me provoque une erreur dès que ma variable est mise a jour :
this class is not key value coding-compliant for the key selection.cheminPhoto

j'ai du mal a voir ce qui ne marche pas.

Réponses

  • CéroceCéroce Membre, Modérateur
    août 2010 modifié #2
    Le NSArrayController n'observe pas la vue, seulement le modèle.
    Il faut donc écrire:

    <br />- (void) setPathToPicture:(NSString *)newPathToPicture<br />{<br />	[pathToPicture release];<br />	pathToPicture = [newPathToPicture copy];<br />	<br />	[observedObjectForPathToPicture setValue:pathToPicture forKeyPath:keyPathForPathToPicture];<br />}<br />


    observedObjectForPathToPicture et keyPathForPathToPicture sont les objets qui ont été passés à  -[bind:toObject:withKeyPath:options:] et que tu as mémorisé, comme indiqué dans le Cocoa Binding Guide. Il faut faire passer la valeur par le NSValueTransformer avant, s'il y en a un.

    P.S.: Peut-être que ta vue fait autre chose, mais il existe une classe NSPathControl.
  • pixmanpixman Membre
    19:24 modifié #3
    Salut !

    Effectivement, il manquait bien l'enregistrement en retour.

    j'ai donc ajouté ceci au code de ma vue personalisée :
    <br /><br />- (void)bind:(NSString *)binding toObject:(id)observableController withKeyPath:(NSString *)keyPath options:(NSDictionary *)options{<br />	<br />	if (binding == @&quot;pathToPicture&quot;) {<br />		[observableController addObserver:self forKeyPath:keyPath options:(NSKeyValueObservingOptionNew |NSKeyValueObservingOptionOld) context:NULL];<br />		<br />		<br />		observedObjectForPathToPicture = observableController;<br />		keyPathForPathToPicture = keyPath;<br />	}<br /><br />}<br />


    mon setPathToPicture a aussi été modifié comme ceci :
    <br />- (void) setPathToPicture:(NSString *)newPathToPicture<br />{<br />	[pathToPicture release];<br />	pathToPicture = [newPathToPicture copy];<br />	<br />	[observedObjectForPathToPicture setValue:pathToPicture forKeyPath:keyPathForPathToPicture];<br />}<br /><br />


    j'ai aussi ajouté le code pour mettre à  jour la propriété de ma vue personalisée :
    <br /><br />- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{<br />	<br />	if (keyPath == keyPathForPathToPicture ) {<br />		pathToPicture = [change valueForKey:@&quot;new&quot;];<br />	}<br /><br />}<br /><br /><br />


    mais le dictionnaire me renvoi des clefs NULL, alors qu'il y a bien une valeur existante dans la sélection de mon NSArrayController.

  • CéroceCéroce Membre, Modérateur
    19:24 modifié #4
    C'est un bug de NSArrayController. C'est une info importante, pourtant passée sous silence dans la doc d'Apple, mais effectivement, NSArrayController n'est pas capable de donner la liste des changements, tu peux seulement connaà®tre la valeur actuelle (ce qui est bien suffisant dans la plupart des cas):

    <br />- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context<br />{<br />	if(context == PathForPictureContext)<br />	{<br />		NSValue *value = [object valueForKeyPath:keyPath];<br />		// À transformer avec le NSValueTransformer; voir le guide<br />


    Ce site est indispensable pour tout ce qui est bindings.
    Regarde les codes d'exemple (Joystick), tu verras qu'ils utilisent le context pour éviter d'appeler [binding isEqualToString:] à  chaque notification KVO.

    Par ailleurs dans la méthode -[bind...], il ne faut pas écrire
    [tt]if (binding == @pathToPicture)[/tt]
    mais
    [tt]if ([binding isEqualToString:@pathToPicture])[/tt]
  • pixmanpixman Membre
    19:24 modifié #5
    Super, effectivement c'était bien ça !

    Merci pour le lien, je suis tombé dessus assez souvent lors de mes recherches, il est vraimant utile.
  • Moi aussi je n'arrête pas de tomber dessus, mais maintenant, je ne le trouve pas du tout utile.
  • CéroceCéroce Membre, Modérateur
    Oui, le lien est mort depuis plus d'un an... malheureusement, je n'ai pas retrouvé la page.



    Si tu as des questions, tu peux toujours me les poser, il y a un moment, je me tâtais même à  écrire un livre au sujet des bindings (et puis comme d'habitude, le manque de temps).
  • J'ai retrouvé le tout avec web archive.

    Mon problème était d'avoir un équivalent aux NSFetchedResultsController : avoir un NSArray d'objets Core Data, avec notifications automatique en cas de changement du contenu, sans utiliser de binding dans un XIB.



    Au final, parce que je n'ai pas le temps de chercher comment ça fonctionne, j'ai commencé à  partir sur des notifications à  l'ancienne, avec le notification center.
Connectez-vous ou Inscrivez-vous pour répondre.