[Enfin Résolu] Disparition de FileOwner's : De xib à  Storyboard

iLandesiLandes Membre
février 2015 modifié dans API AppKit #1

Petite bidouille du week-end.


 


J'essaye de me mettre au storyboard sous Xcode 6 pour le dev' d'application Mac. Je tente de faire un projet tout simple avec gestion de document en Swift, avec la classe NSDocument.


 


Je bug sur le storyboard. Avant il y avait un file owner lié à  la class document.


 


Mais comment fait-on aujourd'hui pour créer l'IBOutlet dans la class document. J'y arrive dans le viewcontroller mais pas dans le document.


 


Peut-être faut-il récupérer dans Document.swift la valeur de l'IBOutlet du ViewController ?


 


J'suis un peu perdu, je comprends plus rien, d'avance merci de votre aide


 


s


e


b


 


--


Le projet est terminé et disponible sur GitHub. Un immense merci à  tous les contributeur(e)s


«1

Réponses

  • J'ai dû me battre aussi avec ce nouveau "mode".


    Avant de lire ce qui suit rappelle toi juste que pour le moment un bug empêche d'utiliser le storyboard et le sandboxing dans un même projet. Cela bloque l' ouverture/sauvegarde des fichiers.


    (mais Apple a la réponde: utiliser les XIB à  la place... connards...)


     


    Pas peur ? Ok !


     


    Pour commencer il faut voir chaque scène comme un XIB à  part entière, dont rien ne rentre et rien ne sort directement. 


    Cela implique une chose importante: les scènes sont l'une à  côté de l'autre mais elle ne peuvent pas interagir sans segue ou glue code


     


    Ce qui veut dire que tu as soit une vue qui est affichée par un segue soit une vue instanciée avec instantiateControllerWithIdentifier:


     


    Maintenant qu'est-ce que tu ferais dans un cas où tu as 2 XIB qui doivent communiquer ? 


    • Mettre un file's owner qui est un NSViewController généralement.
    • Quand tu instancies la vue d'un XIB tu donne une référence aux objets utiles du XIB appelant. 

    Ben là  c'est pareil. Une scène est owned par un NSWindowController ou un NS*ViewController (Il y en a 4 certains sont buggés d'autres très buggés).


    Regarde la doc sur les segues c'est sensiblement pareil.


    Garde juste à  l'esprit qu'il vaut mieux oublier les segue customs. Les infos intéressantes comme la collection des segues connectés à  un control sont privées.


     


    A l'heure actuelle des choses je déconseille fortement l'utilisation des storyboards. Si ça te donne quand même envie de t'y frotter je répondrai à  autant de questions qu'il sera possible 😉 Personnellement je regrette d'avoir démarré un projet avec...


     


     


  • Merci de ta réponse qui m'éclaire un peu


     


    J'ai donc tenté de mettre dans ma scène un Object représentant la classe Document.


     


    Le problème est que du coup j'ai l'impression que ma scène possède 2 documents.


     


    Complètement embrouillé. Je cherche désespérément un projet tout simple en swift avec storyboard et enregistrement de document mais je ne trouve rien ;)


  • Joanna CarterJoanna Carter Membre, Modérateur

    Bonjour Seb


     


    D'abord, la différence essentielle entre les XIB et les storyboards :


     


    Chaque XIB symbolise une vue et il faut "connecter" cette vue à  un File's Owner externe, voir un view controller. Normalement, on fait la connexion, soit dans une méthode "usine" dans la classe du view controller, soit dans le code qui crée le view controller.


     


    Dans un storyboard, il est impossible de mettre une vue dans le storyboard sans un contrôleur. Du coup, l'equivalent de File's Owner, c'est le contrôleur qui "encadre" la vue.




  • J'ai donc tenté de mettre dans ma scène un Object représentant la classe Document.


     


    Le problème est que du coup j'ai l'impression que ma scène possède 2 documents.




     


    C'est le cas. Pour faire plus simple j'écris un sample commenté. J'en ai pour une petite demi-heure  

  • Joanna CarterJoanna Carter Membre, Modérateur
    janvier 2015 modifié #6


    J'ai donc tenté de mettre dans ma scène un Object représentant la classe Document.




     


    C'est pas nécessaire




    Le problème est que du coup j'ai l'impression que ma scène possède 2 documents.



     

    Car t'en as créé deux  :-*



  • C'est le cas. Pour faire plus simple j'écris un sample commenté. J'en ai pour une petite demi-heure  




     


    Super sympa MERCI

  • Une grosse demi heure plus tard...


    Tu trouveras le code complet dans le zip en attachement.


    C'est très simple et ça manipule 2 couleurs.


     


    La partie de code la plus importante est dans Document.swift :



    override func makeWindowControllers() {
    // Returns the Storyboard that contains your Document window.
    let storyboard = NSStoryboard(name: "Main", bundle: nil)!
    let windowController = storyboard.instantiateControllerWithIdentifier("Document Window Controller") as NSWindowController
    self.addWindowController(windowController)

    // Ici on va connecter le representedObject du viewController sur le document.
    var vc = windowController.contentViewController as ViewController
    vc.representedObject = self
    }

    Ce qui veut dire en bon français que tu as une classe ViewController celle-ci subclass NSViewController et elle est en fait le File's owner de la vue qui comporte les 2 colorwells. Elle est représentée par le carré blanc dans le rond bleu.


    Comme tous les NSViewControllers elle a une propriété representedObject, on s'en sert pour donner une référence au document. 


     


    J'ai un peu de mal à  expliquer clairement. Je te conseille de jouer un peu avec le code tu comprendras mieux 😉 


     


  • Super, je regarde cela


  • Mon projet dominical avance, grâce à  vous


     


    M.E.R.C.I


     


    J'y suis presque. J'ai maintenant un petit problème sur mon IBAction qui ne se déclenche qu'à  la fin de l'édition. Du coup ma variable locale dans Document ne se met pas toujours à  jour au bon moment lorsque je sauvegarde.


     


    Ci-joint mon projet


     


  • Je sais pas trop ce que tu veux faire mais un NSTextField pour taper un texte aussi grand c'est pas adapté.


    Tu ne voulais pas utiliser un NSTextView à  la plutôt ?


  • Effectivement un NSTextView est bien mieux adapté mais du coup il n'y plus d'IBAction pour mettre à  jour Document


  • Joanna CarterJoanna Carter Membre, Modérateur



    override func makeWindowControllers() {
    ...
    // Ici on va connecter le representedObject du viewController sur le document.
    var vc = windowController.contentViewController as ViewController
    vc.representedObject = self
    }



     


    Petite question - pourquoi utiliser un var au lieu d'un let - on ne veut pas changer l'objet, seulement une de ses propriétés ?

  • Joanna CarterJoanna Carter Membre, Modérateur


    Effectivement un NSTextView est bien mieux adapté mais du coup il n'y plus d'IBAction pour mettre à  jour Document




     


    Ce n'est pas nécessaire ; essaies-tu à  modifier le texte et tu verras que l'état du document changera, selon la barre de titre.



  • Petite question - pourquoi utiliser un var au lieu d'un let - on ne veut pas changer l'objet, seulement une de ses propriétés ?




     


    Effectivement ! Mais comme j'ai codé ça vite faite j'ai pas fait attention, une relecture plus attentive du texte m'aurait fait mettre un let.


     


    Maintenant dans ce cas présent ça n'a aucune importance vu que ce code ne sera appelé seulement une fois par instanciation de document et de plus le compilo a très certainement optimisé la chose.


     


    Mais c'est une bonne habitude à  avoir ça évite surtout de modifier l'objet.

  • Ce n'est pas nécessaire ; essaies-tu à  modifier le texte et tu verras que l'état du document changera, selon la barre de titre.




    Joanna, merci pour ton aide. Tu as raison pour l'utilisation du Let !


    Par contre l'IBAction permettait de mettre à  jour une variable dans la classe Document et c'est cette variable qui est sauvegardée. Je ne vois pas trop comment m'en sortir. Peut être avec les setters et ou le binding. Cela fait beaucoup de notions nouvelles en Swift pour intégrer tout cela et je m'y perd.
  • Moi je te dirai d'abord de t'intéresser à  NSTextViewDelegate. J'ai comme l'impression que la solution va te sauter aux yeux  


    Maintenant si la dynamique c'est d'apprendre le swift attaque toi à  plus simple car tu vas rencontrer des cas de figures complexes à  cause de Swift qui interagi parfois bizarrement avec Cocoa. Autant maà®triser les techniques que tu veux mettre en oeuvre dès lors...


  • Définitivement je ne suis pas rendu les storyboard. Le but était de faire un truc hyper simple en Swift. J'ai des notions de swift et pour moi traduire un truc aussi simple m'apparait aujourd'hui compliqué. Non pas à  cause de Swift mais à  cause des stroryboards. Je pensais que le story m'aurait facilité la vie mais non. J'ai fait le même projet avec Swift / Xib en 20 minutes.


     


    Je souhaite juste rendre accessible dans ma Classe Document le contenu d'une vue, ce que je fais généralement et simplement avec un IBOutlet.


     


    Quand au delegate, j'en utilise régulièrement mais la franchement dans NSTextViewDelegate je ne vois rien sur le contenu. Cela sert surtout à  gérer les glisser déposer, les sélections, les tool tip, les copier/coller etc. A moins que ce ne soit mes yeux ?

  • Joanna CarterJoanna Carter Membre, Modérateur


    Définitivement je ne suis pas rendu les storyboard. Le but était de faire un truc hyper simple en Swift. J'ai des notions de swift et pour moi traduire un truc aussi simple m'apparait aujourd'hui compliqué. Non pas à  cause de Swift mais à  cause des stroryboards. Je pensais que le story m'aurait facilité la vie mais non. J'ai fait le même projet avec Swift / Xib en 20 minutes.


     


    Je souhaite juste rendre accessible dans ma Classe Document le contenu d'une vue, ce que je fais généralement et simplement avec un IBOutlet.




     


    Il me semble que, en faisant les choses simples, tu pouvais les faire plus compliqués ? As-tu utilisé les bindings pour connecter le NSTextView à  un NSAttributedString ? Si oui, beaucoup de choses deviennent plus simple.


     


    Il n'est pas nécessaire à  détecter les changements dans le NSTextView car le document prend charge d'écouter au moindre changement, en se marquant "dirty".


     


    À part du texte, qu'est-ce que tu veux d'autre dans le document ?

  • Le voilà  le soucis: le non-respect du pattern MVC


     


    - Ton modèle (ici ton document) ne devrait pas connaà®tre ta vue et ta vue ne devrait pas connaà®tre ton modèle.


    - Tu as un NSViewController, il a le rôle de contrôleur, c'est lui qui doit faire le lien entre la vue et le modèle.


     


    Dans l'exemple que je t'ai donné tu as la classe viewController qui est un contrôleur :


     


    - Il a connaissance du modèle par le biais de sa propriété representedObject qui y fait référence. Le modèle pour sa part a connaissance du contrôleur via la méthode makeWindowControllers(). Et mieux, c'est lui qui l'instancie. (C'est une particularité du document-pattern qui dérive pourtant bien de MVC et s'y conforme fortement.)


    - Le contrôleur a aussi connaissance de la vue ainsi que la vue du modèle. Dans ce cas présent c'est via les IBOutlet et IBAction que tu connais bien ou les méthodes déléguées encore. On peut aussi citer les bindings mais il faut un NSObjectController pour plein de raisons. Cela dit ça reste un contrôleur.


     


    Pour résumer : 


     


    modèle ←→ contrôleur ←→ vue


     


    Pour le NSTextViewDelegate il fallait bien ouvrir les yeux, j'avoue... Il s'avère qu'il se conforme à  NSTextDelegate qui contient des jolies choses comme textDidChange() qui s'avère très pratique dans ton cas. 


     


    ça donnerai un truc comme ça :


     


    Document "setRepresentedObject(self)→ ViewController "setString(representedObject.string)→ NSTextView


    Pour ce qui est modèle → vue


    et


    Document ←setString(textView.string)" ViewController ←textDidChange()NSTextView


    Pour vue → modèle


     


    PS: si je mets des setProperty() c'est pour aider à  la lisibilité, on reste on swift donc on utilise la syntaxe pointée, naturellement.


  • iLandesiLandes Membre
    janvier 2015 modifié #21

    Merci beaucoup Pyroh,

     

    J'y suis presque, j'ai défini le delegate de mon NSTextView dans le storyboard comme étant mon ViewController. J'ai fait glisser l'outlet delegate vers ViewControllerScene.

     

    J'ai changer le code de mon viewController



    class ViewController: NSViewController, NSTextViewDelegate {

    //.....


    func textDidEndEditing (notification: NSNotification)
    {
    println("textDidEndEditing")
    }

    func textView(aTextView: NSTextView,
    willChangeSelectionFromCharacterRange oldSelectedCharRange: NSRange,
    toCharacterRange newSelectedCharRange: NSRange) -> NSRange
    {
    println("willChangeSelectionFromCharacterRange")
    return newSelectedCharRange
    }
    }

     

    Je vois bien dans la console le message willChangeSelectionFromCharacterRange mais jamais textDidEndEditing

     
    ;D B)


     


    Le projet est accessible et mis à  jour sur github à  l'adresse suivante :


     


    https://github.com/iLandes/SwiftBaseDocumentSample


  • iLandesiLandes Membre
    janvier 2015 modifié #22

    Cette fois c'était mes yeux ;)


     


    Je clean le projet et le met en ligne sur GitHub !


     


    Un grand merci à  tous  :p   :D


     


    Je viens de rajouter une serialization JSon, n'hésitez pas à  proposer des modifications sur github si c'est pas clair. Je vais grâce à  vous pouvoir m'attaquer à  des projets plus complexe en swift et en storyboard. 


  • Bah voilà , finalement tout est bien qui fini bien !


    Content de t'avoir aidé.  ^_^


  • Il me manque encore un petit truc : avec un NSTextFieldCell je cherche à  valider la saisie avant de sauvegarder et obtenir le déclenchement du délégué : controlTextDidEndEditing. Il me semble qu'il y a un truc a faire avec la chaine des répondeurs pour valider la saisie mais je ne trouve pas où j'ai vu cela...


  • J'ai retrouvé en Cocoa avec Xib je faisais un truc du genre


     


    [myWindow enEditingFor:nil]


     


    Afin de terminer la saisie avant de sauvagarder. Mais en Swift et avec les storyboards comment faire ??


  • Je pensais découvrir swift et storyboard facilement avec ce petit exemple (un simple éditeur de texte avec deux données). Je croyais que cela être simple car je me souviens avoir fait il y a quelque temps ce genre d'exercice sans aucune ligne de code, rien que du clickodrome ^^


     


    J'ai finalement pas mal galèrè et j'ai dû aussi replonger dans pas mal de notion. Voici la dernière que je viens d'implémenter dans le projet disponible sur GitHub.


     


    https://github.com/iLandes/SwiftBaseDocumentSample.git


     


    Un grand merci à  tous ! Ce code est à  votre disposition. Il est bourrée de println() afin d'aider à  la compréhension.


     


    Votre avis sur la qualité du code est souhaitable. 


     


    Il manque une dernière fonctionnalité : Quand je modifie le text du NSTextView, la fenêtre indique que le document est édité mais pas lorsque je modifie la checkbox ou le textfield rien n'indique la modification du document.


     


    Si vous avez une piste, cela permettra de finaliser le projet.


  • Juste une question avant même de m'attaquer à  ta dernière requête...


    C'est quoi cette idée de binder sur les Cells ?!  :p


  • L'idée est de récupérer le contenu de la cell et de l'envoyer directement dans Document à  l'aide d'un didSet et réciproquement, au chargement d'un document, mettre à  jour la vue via le view controller.


     


     


    cf le code


     


     


    ps : un peu tôt pour boire :p   


  • PyrohPyroh Membre
    février 2015 modifié #29

    Oui ça je l'ai bien compris.


    Je reformula ma question alors: " C'est quoi cette idée de binder sur les Cells plutôt que sur les Controls directement ?!  :p "


    Je fais pas mal de modifications à  ton code je fais une pull request directement sur le repo ?


  • Effectivement c'est un peu le problème du clickodrome.


     


     


    Si F Kordon passe par là  il va bien rigoler


     


    Pas de pb pour le pull request ! 


  • Je fais en sorte que ça soit vraiment propre avant de poster le code. (au passage je m'amuse, j'avoue)


     


    En attendant il me parait important de te dire que quand tu veux utiliser des propriétés avec des frameworks et protocols Obj-c (KVO et KVC en font partie) marque les "dynamic".  


    Sinon ça marche pas...

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