Ecrire et lire un banal NSDictionary dans le Pasteboard

berfisberfis Membre
mars 2017 modifié dans Dev. macOS #1

Bonjour,


 


J'ai des objets visuels pour représenter des entités Core Data. Ces objects visuels peuvent être placés sur une pageView, un autre objet visuel, et édités (déplacés, redimentionnés, etc.).


 


Je parviens même à  les stocker comme "modèles" ou "patrons" dans mon fichier de préférences. Pour cela, je transforme l'entité CD en un NSDictionary, en sérialisant les attributs (comme fonte et couleur) en NSData (fig. 1). Cela fonctionne à  ravir.


 


Pour rendre l'application plus complète, je souhaite pouvoir faire du copier/coller entre deux documents (pas d'une application à  l'autre: les objets ont un format propriétaire). J'ai donc essayé la même stratégie (fig. 2)


 


C'est là  que ça se complique. Pas moyen d'enregistrer un NSDictionary dans le presse-papiers. J'obtiens l'erreur:


__NSDictionaryM does not implement the NSPasteboard Writing protocol


Si les éléments contenus dans un dictionnaire (NSString, NSNumber etc.) semble bénéficier de ce protocole, ce n'est pas le cas de NSDictionary...


 


Je me tourne alors vers ce qui semble être une classe "wrapper": NSPasteboardItem, qui semble être un "emballage" (à  la façon d'une NSValue), mais j'ai dû mal comprendre, car j'obtiens aussi un résultat insatisfaisant: NSPasteboardItem est bien collé dans le presse-papiers, mais plus rien n'en ressort " j'ai sans doute mal lu la doc, mais elle manque cruellement d'exemples simples, et je n'ai rien trouvé de probant sur Google (fig. 3)


 


Pourquoi NSDictionary, qui pourtant peut être sans autre archivé dans une plist, rencontre-t-il de tels problèmes pour être écrit et relu dans le presse-papier ?


 


Fais-je fausse route ? Y a-t-il plus simple ? Quelque chose qui rappellerait le bon vieux:


 


FUNCTION PutScrap (length: LONGINT; theType: ResType; source: Ptr) : LONGINT; 


ou son équivalent vingt ans plus tard ?


 


Merci d'avance


Réponses

  • Hello,


     


    Visiblement il faut mettre le protocole NSPasteboardWriting,NSPasteboardReading


     


    Une piste ici: http://stackoverflow.com/questions/28656562/storing-and-retrieving-a-custom-object-from-nspasteboard

  • devulder,


     


    J'ai vu cet exemple (comme d'autres) et je n'ai pas compris grand-chose, je l'avoue.


     


    J'ai un arraycontroller qui va recevoir de la pageView (via un protocole) l'ordre de copier/coller ses entités sélectionnées (self.selectedObjects), voir schéma.


     


    Dés lors, je sérialise mes MO pour en faire des NSDictionaries coupés du MOC. Il me faut maintenant les coller dans le NSPasteboard. Où placer la classe MyClassObject donné par l'exemple que tu cites ?


  • CéroceCéroce Membre, Modérateur
    Je ne suis pas certain que ce soit la bonne manière de faire, mais habituellement, je sérialise le dico en utilisant NSArchiver. On obtient ainsi une NSData qu'on peut mettre dans le Pasteboard.
  • Bonjour Céroce,


    Question de novice: si je veux écrire une NSArray de NSDictionary, je sérialise l'array d'un bloc, ou l'array et chaque dictionary qu'elle contient ?


  • CéroceCéroce Membre, Modérateur
    Tu peux sérialiser l'array.
  • Joanna CarterJoanna Carter Membre, Modérateur

    Autre idée. Pourquoi pas mettre l'objectID de l'entité CoreData sur le Pasteboard ? Ce n'est qu'un String. Puis, on peut récupérer l'objet de sa en le collant.


  • Je pense que Befis veut copier des objets entre deux documents distincts.


    Et, a priori les deux documents ne partagent pas le même MOC.


  • Joanna CarterJoanna Carter Membre, Modérateur

    Peut-être mais le objectId se trouve dans le persistentStoreCoordinator.


     


    Et je pensais de récupérer le objet, le copier dans un nouveau et le modifier si nécessaire. 


  • colas_colas_ Membre
    mars 2017 modifié #10
    Pareil,

    Deux documents différents a priori n'ont pas le même persistentCoordinator.
  • berfisberfis Membre
    mars 2017 modifié #11

    Bonjour Joanna,


     


    Autre problème: Imagine que je modifie l'objet copié avant de le coller. Je vais récupérer l'état actuel lors du collage, or c'est l'état qu'il avait au moment de la copie qui m'intéresse...


  • berfisberfis Membre
    mars 2017 modifié #12

    Ce n'est toujours pas gagné.


     


    pour créer un dictionnaire à  partir de mon entité, je fais ceci:



    - (NSDictionary*) copyToDictionary
    {
    NSDictionary *dictionary = [[NSEntityDescription entityForName:@Reserve inManagedObjectContext:self.managedObjectContext] attributesByName];
    for (NSString *key in dictionary)
    {
    id thisObject = [self valueForKey:key];
    if (thisObject)
    {
    if (![[dictionary valueForKey:key] attributeValueClassName])
    thisObject = [NSKeyedArchiver archivedDataWithRootObject:thisObject];
    [dictionary setValue:thisObject forKey:key];
    }
    else
    {
    [dictionary setValue:nil forKey:key];
    }
    }
    return dictionary;

    ça marche une fois, si je répète l'opération quelques secondes plus tard ça plante:



    2017-03-12 09:55:02.549 Hierarchy[8443:4564743] -[NSConcreteMutableData attributeValueClassName]: unrecognized selector sent to instance 0x6000004401b0
    2017-03-12 09:55:02.549 Hierarchy[8443:4564743] -[NSConcreteMutableData attributeValueClassName]: unrecognized selector sent to instance 0x6000004401b0
    2017-03-12 09:55:02.551 Hierarchy[8443:4564743] (
    0 CoreFoundation 0x00007fff9d8b8452 __exceptionPreprocess + 178
    1 libobjc.A.dylib 0x00007fff9b7e373c objc_exception_throw + 48
    2 CoreFoundation 0x00007fff9d92210d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
    3 CoreFoundation 0x00007fff9d8284d1 ___forwarding___ + 1009
    4 CoreFoundation 0x00007fff9d828058 _CF_forwarding_prep_0 + 120
    5 Hierarchy 0x0000000100009db6 -[Reserve copyToDictionary] + 774

    ... et je ne vois pas pourquoi.


     


    EDIT:  Ah si, je vois. La version finale est :



    - (NSDictionary*) copyToDictionary
    {
    NSDictionary *dictionary = [[[self entity]attributesByName]copy];
    for (NSString *key in dictionary)
    {
    id thisObject = [self valueForKey:key];
    if (thisObject)
    {
    if (![[dictionary valueForKey:key] attributeValueClassName])
    thisObject = [NSKeyedArchiver archivedDataWithRootObject:thisObject];
    [dictionary setValue:thisObject forKey:key];
    }
    else
    [dictionary setValue:nil forKey:key];
    }
    return dictionary;
    }

    la propriété attributesByName est (readonly, copy) mais on peut modifier les clés du dictionnaire ! Je repars d'une copie fraà®che à  chaque fois maintenant.


  • MayerickMayerick Membre
    mars 2017 modifié #13

    Hello,


     


    J'utilise la méthode conseillée par Céroce dans mon application. J'avais trouvé ce code sur le net et il m'avait bien aidé à  comprendre le principe. Ici par exemple je sérialise l'array qui contient des custom objects qui contiennent mes dictionnaires:



    -(NSMutableArray*)dataSelection {
    NSMutableArray <CustomObject*> *customObjectArray = [NSMutableArray new];

    /* some logic to get the selection */
    CustomObject *customObject = /* ... */;
    customObject.dictionnary = /* ... */ ;
    [customObjectArray addObject:customObject];
    /* some logic to get the selection */

    return customObjectArray;
    }

    -(void)copy:(id)sender {
    NSArray *rootObject = [self dataSelection];
    UIPasteboard *board = [UIPasteboard pasteboardWithName:@myPasteboard create:YES];
    NSData *tileData = [NSKeyedArchiver archivedDataWithRootObject:rootObject];
    if (tileData) {
    [board setData:tileData forPasteboardType:@MyUniformTypeIdentifier];
    }
    }

    -(void)paste:(id)sender {
    UIPasteboard *board = [UIPasteboard pasteboardWithName:@myPasteboard create:NO];
    NSArray *pbType = [NSArray arrayWithObject:@MyUniformTypeIdentifier];

    if ([board containsPasteboardTypes:pbType]) {
    // get data :
    NSData *tileData = [board dataForPasteboardType:@MyUniformTypeIdentifier];
    NSArray <CustomObject*> *customObjectArray = (NSArray *)[NSKeyedUnarchiver unarchiveObjectWithData:tileData];

    if (customObjectArray) {
    /* Do whatever you want with customObjectArray */
    }
    }
    }


    Une fois que tu as ton dictionnaire, est-ce que cela ne ferait pas le job ?


  • berfisberfis Membre
    mars 2017 modifié #14

    Bonsoir,


     


    J'utilise le code suivant, assez proche de ce que tu m'indiques (je ne le connaissais pas, mais je suppose qu'il n'y a pas trente-six manières de faire le travail):



    - (void) copySelectionToPasteboard
    {
    NSPasteboard *board = [NSPasteboard generalPasteboard];
    [board clearContents];
    [board setData:[NSKeyedArchiver archivedDataWithRootObject:[self dictionariesFromSelection]] forType:@RSRV];
    }

    - (void) pasteFromPasteboard
    {
    NSPasteboard *board = [NSPasteboard generalPasteboard];
    NSData *data = [board dataForType:@RSRV];
    NSArray * array = (NSArray*) [NSKeyedUnarchiver unarchiveObjectWithData:data];
    [self addReserveUsingDictionaries:array];
    }

Connectez-vous ou Inscrivez-vous pour répondre.