Differences NSWindow / NSWindowController ?

AdauAdau Membre
02:58 modifié dans API AppKit #1
Bonjour,

Après avoir bien fini le livre de notre cher Aaron, il y a toujours un point sur lequel je bute:
Quels sont les differences entre les classes NSWindow et NSWindowController ?

Ces deux classes permettent d'afficher des fenêtres. Deux instances de ces classes qui recoivent "showWindow:self" affichent la fenêtre qui leur est associé.
En regardant la documentation, je n'arrive pas trop à  comprendre les subtilités.

Au passage, j'ai un peu la même question pour NSView (dont je comprend très bien son rôleà  et NSViewController (dont je comprend rien).

Merci d'avance !

Adau

Réponses

  • Philippe49Philippe49 Membre
    02:58 modifié #2
    C'est la différence qu'il y a entre une voiture et son conducteur.
    La Window est un objet graphique, le WindowController en décide le comportement.
  • AdauAdau Membre
    02:58 modifié #3
    Je comprend bien.
    Mais dans quels cas utiliser un NSWindowController ou un NSWindow ?
  • Philippe49Philippe49 Membre
    mai 2009 modifié #4
    Dans tous les cas tu as besoin de la NSWindow et des NSView.
    Après, il faut se poser la question de la distribution des rôles dans ton programme. Pour des programmes relativement simples, cela ne pose pas trop de problème de faire gérer la fenêtre au contrôleur général de l'application, mais pour des programmes plus complexes, ou pour des classes que l'on veut réutilisables (un inspecteur, un panel de choix, une vue au design spécialisé, ...), on peut faire, comme dans le ch 29 de Hillegass, un découpage en
    - un fichier xib pour le design de la vue (resp fenêtre)
    - une classe pour le controller représenté par le File's Owner du xib, et créé par ailleurs dans le code
    - une classe pour la vue (et son contenu) que l'on ajoute au besoin sur la fenêtre principale.
  • CéroceCéroce Membre, Modérateur
    02:58 modifié #5
    dans 1242150669:

    Mais dans quels cas utiliser un NSWindowController ou un NSWindow ?

    Tu utilises toujours les deux.

    Souvent, un NSWindowController est créé sans que tu t'en rendes compte. Par contre, passer par un NSWindowController est le seul moyen de charger une fenêtre depuis un .nib.
  • FloFlo Membre
    02:58 modifié #6

    NSWindowController est le seul moyen de charger une fenêtre depuis un .nib.


    Ha ? Quand je fais ça dans mon controller perso (qui n'hérite pas de NSWindowController) ça passe quand même par un NSWindowController ?

    <br />[NSBundle loadNibNamed: &quot;aFile&quot; owner: self]<br />
    


    Il me semble que par cette methode mon controller devient le file owner du nib qui contient un NSWindow et peut la gérer sans passer par un NSWindowController...

  • CéroceCéroce Membre, Modérateur
    mai 2009 modifié #7
    Bon ben j'ai tort alors.
  • psychoh13psychoh13 Mothership Developer Membre
    02:58 modifié #8
    NSWindowController n'est pas le seul moyen de charger une fenêtre venant d'un xib/nib. Mais par contre ça facilite la gestion d'une fenêtre chargé depuis un xib/nib.

    Je réitère le conseil de Philippe49. Et même j'irai plus loin en disant qu'il vaut mieux utiliser un XIB par fenêtre et par vue interchangeable avec les contrôleurs correspondant (NSWindowController pour un xib de NSWindow et NSViewController par xib de NSView).

    Pour la gestion des différents éléments graphiques c'est beaucoup plus simple, plus efficace et plus clair. Et je conseille même de faire des allocations/désallocations à  la volée, c'est-à -dire que tu alloues le contrôleur et instancies le XIB seulement au moment où il va être afficher, et quand il ne sera plus affiché, tu le supprimes complètement, ça peut sembler coûteux en mémoire mais ce n'est pas vraiment quelque chose de gênant de nos jours, surtout que cette mémoire est recyclée.

    Alors vous pouvez quand même vous amusez à  stocker les contrôleurs au fur et à  mesure qu'ils sont chargés, mais la gestion des objets en mémoire devient alors un vrai casse-tête. Personnellement, j'ai vite abandonné cette idée.

    Pour ce qui est des NSViewController, je précise un peu. Quand on a par exemple une fenêtre de préférences avec plusieurs panneaux, une bonne technique est de créer un XIB pour une fenêtre avec un NSWindowController comme file's owner, la fenêtre aura une toolbar par exemple, mais la contentView sera complètement vide.

    Au lieu de définir une vue par panneau dans le même XIB et de les mettre dans liste des objets de premier niveau... Une bonne pratique est de créer un XIB par NSView, chaque XIB représentera un panneau de préférences, et chaque XIB contiendra un file's owner de type NSViewController (ou d'une sous classe).

    Avec cette architecture, le NSWindowController des préférences fait la permutation des vues à  la demande. à‰tant donné qu'il doit aussi gérer une NSToolbar, j'ai personnellement opté pour un dictionaire contenant pour chaque NSToolbarItem le nom de l'item, le nom de l'icône, et le nom du XIB à  chargé. Quand on clique sur un item, je sélectionne dans le dictionaire l'objet correspondant à  l'identifiant de l'item et je charge les infos correspondantes. Je ne conserve dans les ivars de mon NSWindowController que le NSViewController affiché, et j'alloue/supprime à  chaque changement d'onglet.
  • 02:58 modifié #9
    dans 1242222147:

    Avec cette architecture, le NSWindowController des préférences fait la permutation des vues à  la demande. à‰tant donné qu'il doit aussi gérer une NSToolbar, j'ai personnellement opté pour un dictionaire contenant pour chaque NSToolbarItem le nom de l'item, le nom de l'icône, et le nom du XIB à  chargé. Quand on clique sur un item, je sélectionne dans le dictionaire l'objet correspondant à  l'identifiant de l'item et je charge les infos correspondantes. Je ne conserve dans les ivars de mon NSWindowController que le NSViewController affiché, et j'alloue/supprime à  chaque changement d'onglet.


    Mouaip sur ce coup je trouve ça un peu débile l'histoire de charger un NSViewController quand tu changes d'item sur la toolbar.. Autant relâcher carrément le NSWindowController des prefs une fois que la fenêtre est fermée?
  • psychoh13psychoh13 Mothership Developer Membre
    02:58 modifié #10
    Bah pourquoi pas ?
    Si la fenêtre n'est ouverte qu'une seule fois par exécution, la mémoire qu'elle utilise sera disponible pour le reste de l'application...
  • 02:58 modifié #11
    Le truc c'est qu'il y a aussi le fait de "charger" les préférences. C'est à  dire utiliser notre NSUserDefaults pour les faire "apparaà®tre" dans l'UI des preferences (boutons checked, etc...).. Bon tu vas me dire c'est super rapide :D Mais bon perso j'ai vu une appli qui fait comme toi, sauf qu'il y a avait une preference "Theme" avec une preview du theme (à  la Adium).. et j'ai trouvé ça super chiant d'avoir un blocage de 3 secondes à  chaque fois que je switchais sur "Themes".
  • psychoh13psychoh13 Mothership Developer Membre
    02:58 modifié #12
    Après, rien ne t'empêche d'optimiser certaines parties pour lesquelles les problèmes de performance sont trop visibles, mais un développeur devrait savoir qu'il ne faut jamais chercher à  optimiser son code à  l'avance, et qu'il faut attendre de voir si ça a vraiment un impact.

    Il faut d'abord mettre en priorité la clarté et la simplicité du code, et ensuite t'optimise.
  • AdauAdau Membre
    02:58 modifié #13
    Bon, je vais tenter de faire un résumé pour voir si j'ai bien compris. Merci de vos réponses.

    En gros, si je veux afficher une fenêtre, disons la fenêtre de préférences. Dans mon AppController, je mets une variable PreferenceController  d'une sous classe d'un NSWindowController que j'aurai créé. Cette sous-classe jouera le rôle d'un Controlleur "normale" d'une application mais juste pour la partie préférence.
    J'affiche mon fichier xib en invoquant la méthode [instancePreferenceController showWindow:self], le fichier xib ayant pour File's Owner la classe PreferenceController.

    Ce qui me semble bizzare, c'est que si la sous classe n'est plus un NSWindowsController mais juste un NSWindow, ca marche aussi, de la même manière.
  • 02:58 modifié #14
    dans 1242236705:

    Bon, je vais tenter de faire un résumé pour voir si j'ai bien compris. Merci de vos réponses.

    En gros, si je veux afficher une fenêtre, disons la fenêtre de préférences. Dans mon AppController, je mets une variable PreferenceController  d'une sous classe d'un NSWindowController que j'aurai créé. Cette sous-classe jouera le rôle d'un Controlleur "normale" d'une application mais juste pour la partie préférence.
    J'affiche mon fichier xib en invoquant la méthode [instancePreferenceController showWindow:self], le fichier xib ayant pour File's Owner la classe PreferenceController.

    Ce qui me semble bizzare, c'est que si la sous classe n'est plus un NSWindowsController mais juste un NSWindow, ca marche aussi, de la même manière.


    Non ça ne sera pas pareil, tu ne pourras pas créer un "Preferences.xib"... en remplaçant NSWindowController par NSWindow, tu initialises simplement une fenêtre... ce qui est assez idiot si on veut faire des préférences.
    NSWindowController va te permettre de gérer ta NSWindow dans un "Preferences.xib". Ce meme NSWindowController pourra gérer les différents éléments qui composent la fenêtre.
    En gros c'est comme si tu avais un MainMenu + AppController bis.
  • AdauAdau Membre
    02:58 modifié #15
    Ok merci, je comprend déjà  beaucoup mieux.

    Dans ce cas là , quel est le rôle de la classe NSWindow ou de ses sous-classes créées par nos soins ?
  • psychoh13psychoh13 Mothership Developer Membre
    02:58 modifié #16
    Déjà  tu crées rarement une sous-classe de NSWindow, voire même jamais. Au lieu de ça, tu utilises les méthodes déléguées pour changer son comportement, ou bien tu l'instancies manuellement si le XIB ne te permet pas de faire ce que tu veux.

    Ensuite, la class NSWindow c'est l'objet graphique "fenêtre", les instances de NSWindow ce sont les objets qui s'affichent à  l'écran, c'est elle qui reçoit les événements et les transmets à  ses vues, c'est elle qui se ferme ou s'agrandit quand tu appuies sur ses boutons en haut à  gauche.

    Alors que NSWindowController est un objet non-graphique conçu pour contrôler la fenêtre, et cette classe-là  est typiquement conçue pour être sous-classée pour contenir la logique de ton application.
  • 02:58 modifié #17
    dans 1242246480:

    Ok merci, je comprend déjà  beaucoup mieux.

    Dans ce cas là , quel est le rôle de la classe NSWindow ou de ses sous-classes créées par nos soins ?


    On peut définir le sous-classage par remplacement des méthodes déjà  existantes de la classe afin de les améliorer ou de modifier son comportement. Mais il peut se définir aussi par l'ajout de méthodes.

    Imaginons que tu veuilles faire une fenêtre transparente, tu devras sous classer NSWindow et overrider le "init...." de la fenêtre. Imaginons aussi que tu veuilles rajouter une sous-vue à  la "contentView" de ta fenêtre (la contentView d'une NSWindow c'est simplement la vue de la fenêtre comme son nom l'indique, mais sans les Toolbar, bar de titre etc...). Il suffira alors de faire  [[maFenetre contentView] addSubview:otherView]; .. bon soit c'est la meilleure solution... mais vu que tu as déjà  sous-classé la fenêtre en question autant en profiter pour lui rajouter des méthodes? tu rajoutes donc -(void)addSubview:(NSView*)view; dans le header de la sous-classe de NSWindow, puis dans le fichier implémentation tu fais appel à  sa contentView :
    <br />- (void)addSubview:(NSView*)view<br />{<br />&nbsp;  [[self contentView] addSubview:view];<br />}<br />
    


    ça sert à  rien, mais dans ton AppController ça t'éviteras de faire appel à  la "contentView" vu que la sous-classe de la fenêtre se chargera de ça.. suffira juste de faire [myWindow addSubview:otherView];
  • psychoh13psychoh13 Mothership Developer Membre
    02:58 modifié #18
    Mais il est à  noté que tu peux tout faire sans avoir à  sous-classer NSWindow, à  part si vraiment tu veux une fonctionnalité dans la classe elle-même... Mais tous les exemples qu'a cité Eaglelouk peuvent se faire sans sous-classer... Au pire, tu peux rajouter la méthode -addSubview: dans une catégorie de NSWindow, ça t'évite le sous-classage.

    Sous-classer n'a vraiment de sens que si tu veux changer le comportement de la classe, en tout cas dans le cas des classes normales. Par exemple, NSPanel, sous-classe de NSWindow, change le comportement de NSWindow en n'acceptant jamais de devenir la mainWindow de l'application, les instances de cette classe ne seront jamais plus que des keyWindow.
  • FloFlo Membre
    02:58 modifié #19
    A ce sujet, je voulais savoir, dans mon projet j'utilise beaucoup de sheet (NSPanel) pour ajouter, supprimer, mettre à  jour etc...

    Ces sheets ne sont instanciées à  partir d'un xib qu'à  la première demande via la méthode :

    <br /> [NSBundle loadNibNamed: ADD_SYMBOL_XIB owner: self];<br />
    


    Est-ce vraiment nécessaire de passer par un NSWindowController pour chacune des sheets ou alors vaut-il mieux réserver cette classe pour les fenêtres (exceptées les sheets) et les NSView ?
  • 02:58 modifié #20
    dans 1242805841:

    A ce sujet, je voulais savoir, dans mon projet j'utilise beaucoup de sheet (NSPanel) pour ajouter, supprimer, mettre à  jour etc...

    Ces sheets ne sont instanciées à  partir d'un xib qu'à  la première demande via la méthode :

    <br /> [NSBundle loadNibNamed: ADD_SYMBOL_XIB owner: self];<br />
    


    Est-ce vraiment nécessaire de passer par un NSWindowController pour chacune des sheets ou alors vaut-il mieux réserver cette classe pour les fenêtres (exceptées les sheets) et les NSView ?


    à  la manière d'un UIAlertView, tu peux simplement le créer lorsque tu en a besoin puis le release une fois que tu reçois la delegate qui annonce la fin du sheet.
  • FloFlo Membre
    02:58 modifié #21

    à  la manière d'un UIAlertView, tu peux simplement le créer lorsque tu en a besoin puis le release une fois que tu reçois la delegate qui annonce la fin du sheet.


    Toi tu ferais un release sur la sheet quand on la ferme ? C'est pas un peu lourd de recharger le xib à  chaque fois qu'on veut s'en servir ?
    Sinon on passe par un NSWindowController ou pas ?
Connectez-vous ou Inscrivez-vous pour répondre.