[Débutant] Faire le lien entre une NSView et la fenêtre de mon application
colas_
Membre
Bonjour !
Je suis content de rejoindre ce forum.
Je me lance dans la programmation Mac !
Je suis en train de lire le livre de Aaron Hillegass (j'en suis à la page 160 de l'édition 2), mais toute référence que vous jugez bonne m'intéresse !
Voilà le petit exercice que je me suis donné à faire (et que je n'arrive pas à faire)...
Mais je n'y arrive pas.
J'ai trouvé un fil sur ce sujet, plus ou moins, mais je crois que ce n'est pas assez détaillé pour moi. J'ai peur de louper quelque chose avec les frame, ou autre.
J'ai essayé en créant une classe NSView et aussi une NSViewController, mais rien ne marche.
Mon but à terme (l'exercice que je me suis fixé) est de créer une application dont la fenêtre contient un bouton. Quand on appuie sur le bouton, une zone de saisie (typiquement 3/4 NSTextField, etc.) apparaà®t dans la fenêtre. Si je clique plusieurs fois, les zones de saisie apparaissent les unes à la suite des autres, de haut en bas.
Voici mon code :
J'ai aussi essayer avec une classe annexe NSView plutôt que NSViewController.
D'ailleurs, quelle est la différence entre les deux ?
Pour ce problème, j'aurais plutôt tendance à dire que je dois disposer d'une classe NSView.
Voici ce que j'ai essayé :
Merci !
Colas
Je suis content de rejoindre ce forum.
Je me lance dans la programmation Mac !
Je suis en train de lire le livre de Aaron Hillegass (j'en suis à la page 160 de l'édition 2), mais toute référence que vous jugez bonne m'intéresse !
Voilà le petit exercice que je me suis donné à faire (et que je n'arrive pas à faire)...
- J'ai créé une Application Cocoa
- J'ai créé une NSView (tout du moins sa description avec IB)
- Je souhaite qu'au lancement de mon application la vue apparaisse dans la fenêtre !... Et oui aussi simple que ça
Mais je n'y arrive pas.
J'ai trouvé un fil sur ce sujet, plus ou moins, mais je crois que ce n'est pas assez détaillé pour moi. J'ai peur de louper quelque chose avec les frame, ou autre.
J'ai essayé en créant une classe NSView et aussi une NSViewController, mais rien ne marche.
Mon but à terme (l'exercice que je me suis fixé) est de créer une application dont la fenêtre contient un bouton. Quand on appuie sur le bouton, une zone de saisie (typiquement 3/4 NSTextField, etc.) apparaà®t dans la fenêtre. Si je clique plusieurs fois, les zones de saisie apparaissent les unes à la suite des autres, de haut en bas.
Voici mon code :
<br />
//<br />
// AppDelegate.h<br />
// VueDynamique<br />
//<br />
// Created by Colas on 04/03/13.<br />
// Copyright © 2013 __MyCompanyName__. All rights reserved.<br />
//<br />
#import <Cocoa/Cocoa.h><br />
@class Vue2 ;<br />
@interface AppDelegate : NSObject <NSApplicationDelegate><br />
{<br />
IBOutlet NSBox *box;<br />
NSViewController *vueController;<br />
}<br />
@property (assign) IBOutlet NSWindow *window;<br />
@property (assign) NSViewController *vueController;<br />
<br />
@end<br />
<br />
//<br />
// AppDelegate.m<br />
// VueDynamique<br />
//<br />
// Created by Colas on 04/03/13.<br />
// Copyright © 2013 __MyCompanyName__. All rights reserved.<br />
//<br />
#import "AppDelegate.h"<br />
#import "Vue2.h"<br />
@implementation AppDelegate<br />
@synthesize window, box, vueController;<br />
-(id)init<br />
{<br />
vueController = [[Vue2 alloc] init];<br />
<br />
NSLog(@"AppDelegate : création de l'objet");<br />
<br />
NSView *vue = [vueController view] ;<br />
<br />
/*NSRect newFrame = [vue frame];<br />
<br />
<br />
NSLog(@"La vue contrôlée par VueController %@", vue);<br />
NSLog(@"Le frame de cette vue est : %f et %f", newFrame.size.width, newFrame.size.height );<br />
<br />
//[vue viewWillMoveToWindow:window];<br />
[[window contentView] addSubview:vue];<br />
//[vue viewDidMoveToWindow];<br />
<br />
<br />
NSLog(@"La contentView de la fenêtre est %@", [window contentView]);*/<br />
<br />
[box setContentView:vue];<br />
<br />
[super init];<br />
return self;<br />
}<br />
-(void)awakeFromNib<br />
{<br />
NSLog(@"AppDelegate awake from nib");<br />
}<br />
- (void)dealloc<br />
{<br />
[super dealloc];<br />
}<br />
@end<br />
<br />
//<br />
// Vue2.h<br />
// VueDynamique<br />
//<br />
// Created by Colas on 04/03/13.<br />
// Copyright © 2013 __MyCompanyName__. All rights reserved.<br />
//<br />
#import <Cocoa/Cocoa.h><br />
@interface Vue2 : NSViewController<br />
@end<br />
<br />
//<br />
// Vue2.m<br />
// VueDynamique<br />
//<br />
// Created by Colas on 04/03/13.<br />
// Copyright © 2013 __MyCompanyName__. All rights reserved.<br />
//<br />
#import "Vue2.h"<br />
@implementation Vue2<br />
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil<br />
{<br />
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];<br />
if (self) {<br />
// Initialization code here.<br />
}<br />
<br />
return self;<br />
}<br />
@end<br />
J'ai aussi essayer avec une classe annexe NSView plutôt que NSViewController.
D'ailleurs, quelle est la différence entre les deux ?
Pour ce problème, j'aurais plutôt tendance à dire que je dois disposer d'une classe NSView.
Voici ce que j'ai essayé :
<br />
//<br />
// Vue.m<br />
// VueDynamique<br />
//<br />
// Created by Colas on 04/03/13.<br />
// Copyright © 2013 __MyCompanyName__. All rights reserved.<br />
//<br />
#import "Vue.h"<br />
@implementation Vue<br />
<br />
<br />
- (id)initWithFrame:(NSRect)frame<br />
{<br />
NSLog(@"La méthode 'initWithFrame' est appelée");<br />
NSLog(@"Le frame est : %f et %f", frame.size.width, frame.size.height );<br />
NSRect newFrame ;<br />
NSPoint point ;<br />
point.x = 500.0;<br />
point.y=500.0;<br />
NSSize taille;<br />
taille.width = 500.0;<br />
taille.height = 500.0;<br />
newFrame.origin = point;<br />
newFrame.size = taille;<br />
<br />
NSLog(@"Le newFrame est : %f et %f", newFrame.size.width, newFrame.size.height );<br />
self = [super initWithFrame:newFrame];<br />
if (self) {<br />
// Initialization code here.<br />
}<br />
<br />
return self;<br />
}<br />
- (void)drawRect:(NSRect)dirtyRect<br />
{<br />
// Drawing code here.<br />
}<br />
@end<br />
Merci !
Colas
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Pour ajouter une vue dans une autre on utilise la méthode addSubview:
Le plus simple, le plus souvent, une fois définie une sous classe de NSView en redefinissant la méthode drawRect:, on choisit alors dans IB une vue générique et on lui donne la classe Vue2 par exemple.
En faisant ça je crois que tu aurais vu que ta vue ne se dessinait pas faute de lui avoir donné les instructions pour qu'elle le fasse dans drawRect:
On utilise aussi communément la vue contentView qui est ajoutée par Interface Builder à chaque fenêtre comme conteneur principal comme tu le fais dans le passage commenté.
A quoi sert donc ta NSBox ?
As tu vérifié cela ? Tu as l'air d'utiliser d'anciennes versions au vu du code. En principe pour faire ce que tu veux faire il suffit de créer un projet ; un clic.
De quel livre parles tu ? À ma connaissance Aaron Hillegass en a au moins écrit quatre.
Tu as sans doute lu trop vite, ou pas le bon livre, car le motif MVC doit être présenté avant la page 160. C'est un motif fondamental de la programmation Cocoa qui répond à ta question concernant la différence entre NSView et NSViewController.
pour cela, dans 'Library'/'Classes' tu peux glisser un objet (cube) de ta classe vue vers la vue principale,
tu pourras ensuite dessiner dessus
@laudema :
J'explore ta réponse.
Je butte sur la méthode drawRect. Que dois-je mettre dedans ?
Dans mon idée, je dessine la vue avec IB et donc je n'ai pas à coder ce que la vue doit dessiner !
Merci
Ce que j'avais fait, c'est que j'avais défini la classe de "File's owner" à ma classe Vue (qui hérite de NSView).
Est-ce équivalent ?
Qu'est-ce que le "File's owner" ?
Merci !
Oui, tu as raison, je n'utilise pas le même OS, ni la même version de Xcode...
L'édition du livre (Programmation Cocoa sous Mac OSX) est plus ancienne que mes outils !
J'ai dû lire trop vire, oui ; mais j'ai l'impression aussi que dans ce livre, il explique comment faire un projet avec le framework sans expliquer tous les détails. Ce n'est pas clair pour moi la différence entre NSView et NSViewController. Le controller peut intercepter des events, c'est ça ?
Merci !
ça ne marche pas...
Je continue mes essais !
Merci !
Premièrement, tu crée une vue supplémentaire dans ton fichier XIB principal (MainMenu.xib) et tu fixes sa classe à "Vue" (un petit détail, je préfère le dire avant que tu te fasse pourrir par les autres : on donne en général des noms anglais aux variables et aux classes, tout simplement parce-que les accents étant interdits, des noms français seraient bizarres. Disons que ça fait parties de "conventions").Tu as donc une instance de ta classe (un objet) qui se crée au désarchivage du fichier au lancement de ton application. Cet objet n'a aucune liaison avec le reste de l'application. Tu crée en même temps par le code un autre objet (une autre instance) de classe Vue. Mais cet objet, connu du reste de l'application, n'est pas l'objet créé dans IB, même si il est de même classe . Ce qu'il faudrait faire, c'est dans le fichier AppDelegate.h ajouter la ligne
entre les deux accolades sous @interface.Puis tu compile (pommeB). Tu reviens dans IB. Sur le cube bleu "App Delegate", tu fais un clic-droit (ou ctrl-clic) pour faire apparaà®tre la liste des connexions. Tu aura une Outlet intitulée "maVue" qu'il suffira de connecter à la vue en question. Ainsi, ton application connaà®tra CETTE vue et donc pourra l'afficher. Cette ligne crée une variable-pointeur vers la vue que tu as rajouté, ce qui permet à ton application de connaà®tre ta vue.
Deuxième solution, tu crée carrément un autre fichier XIB avec une vue. Puis tu fixes la classe de l'objet File's Owner à Vue. Seulement, pour faire comme ça, ce n'est pas une classe héritée de NSView qu'il te faut mais une classe héritée de NSViewController.
Donc, tu fais comme tu a fais mais avec NSViewController. Ton fichier AppDelegate.m deviendra:
Ou, en respectant les conventions
La méthode drawRect sert à redessiner la vue. C'est une méthode automatiquement rappelée à chaque fois qu'il est nécessaire de rafraà®chir l'affichage. On y met des instructions pour faire des dessins, tracer des courbes ou bien composer une image.
1) Une méthode d'init a une forme idiomatique, qui est:
C'est ainsi et pas autrement, parce qu'il est possible que [super init] renvoie nil si la classe parente n'a pas pu s'initialiser (certes ça n'arrive jamais avec NSObject).
2) La méthode d'initialisation désignée de NSView est -initWithFrame:
Il faut donc donner la frame de la vue lors de sa création. La fonction NSMakeRect() renvoie un NSRect.
3) Par défaut, la méthode -[NSView drawRect:] ne dessine rien. Il est donc normal que tu ne voies rien. Dans ta sous-classe de NSView, écris par exemple:
La raison est que les outlets ne sont pas encore fixées quand -init est appelée.
Implémente cela dans -awakeFromNib, là tu es sûr que les outlets sont fixées.
Disons que c'est un problème parmi d'autres. Mais relire le paragraphe consacré au MVC est effectivement une bonne idée.
Grâce à votre aide, j'ai réussi /smile.png' class='bbc_emoticon' alt=':)' />
Voilà comment faire :
La classe du File's Owner est par défaut NSViewController, on garde ça.
Après coup, ce qui manquait :
Pour preuve, on a une méthode "InitFromNibFile" pour NSViewController mais pas pour NSView...
ça correspond ce que dit jpimbert quand il écrit que la classe du File's Owner est normalement une classe héritant de NSViewController.
Sinon, quelles ressources me conseillez-vous pour apprendre à remplir une fenêtre avec des NSView ?
Concernant la différence NSView / NSViewController, je ne suis pas sûr d'avoir compris.
Mon idée c'est que le contrôleur catch des events.
Mais, pour mon exercice, je voulais juste changer ce qui s'affiche à l'écran.
Merci à vous !
A+
Pour simplifier, on va dire que dans ce cadre-là , ce que tu dis est exact.
(Mais, par exemple, tu noteras qu'un NSWindowController charge aussi un .nib).
Je dirais qu'il faut que tu continues à lire le livre.
À la fin de chaque chapitre, il y a des "challenges" qui se font faisables avec ce que tu as appris. Donc, je te conseille de te limiter à ces challenges. Une fois que tu auras fait le tour du bouquin, tu auras toute liberté. Je sais que c'est un peu frustrant, mais l'auteur anticipe les difficultés que tu auras même en faisant des choses simples. (Je me suis confronté au même problème avec la première édition du livre en... 2001).
Pour résumer: NSView se dessine et réagit aux événements (clavier, souris, etc.).
NSViewController est un Contrôleur dans le modèle MVC, il sert donc à faire le lien entre les Vues et les Modèles.
C'est plutôt faux, parce que ça, c'est le boulot de NSView.
(Mais en pratique, NSViewController hérite de NSRespondeur pour pouvoir être placé dans la chaà®ne des répondeurs et ainsi réagir aux événements qui n'ont pas été traités par les vues).
Il y a une différence majeure entre NSView et NSViewController si on se place dans le concept Modèle/Vue/Controlleur (le MVC)/ Plutôt que de paraphraser, je préfère citer Ali Gator :
Dans quel but ?
Que veux-tu faire exactement ?
Et en quoi les contrôles proposés en standard par Cocoa ne te conviennent pas ?
Non, ce qui "[url="https://developer.ap...00060i-CH3-SW10"]catch des évents[/url]" c'est un NSResponder, une NSView est une sous classe de NSResponder au même titre que NSApp.
NSViewController descend aussi de NSResponder pour pouvoir s'intercaler dans la chaine des répondeur mais il n'a pas pour but de répondre en premier aux évenements, généralement c'est la vue active ou l'application qui réagit en premier, lui est la cible des actions et il fait le lien entre la vue qui affiche et l'objet modèle. D'ailleurs tu as dans NSViewController un outlet [font=courier new,courier,monospace]representedObject[/font]. Or Cocoa est généralement parcimonieux avec les outlets (par rapport à moi /wink.png' class='bbc_emoticon' alt=';)' /> dans mes contrôleurs) donc s'il en met un c'est que c'est important.
Une NSView en a deux : un menu contextuel & la keyView suivante quand on tabule. Elle se dessine et interprète les événements (clavier ou souris) si elle a été configurée pour sinon elle passe au next responder . Beaucoup de NSView dans AppKit sont des NSControl qui eux peuvent enregistrer des actions et des cibles à utiliser en cas d'événement clavier ou souris.
La cible est alors souvent un contrôleur chargé de faire un travail avec le modèle ( le [font=courier new,courier,monospace]representedObject[/font] de NSViewController).
Accessoirement NSViewController est un petit nouveau dans l'écosystème Cocoa, il est apparu bien après les NSWindowController et AppKit existait déjà depuis longtemps .. Si tu as un peu de temps devant toi tu devrais lire ça tout y est à commencer par hiérarchie AppKit (OS X)
PS : une vue principale peut contenir autant de sous-vues qu'on veut, les boutons et autres objets d'interface seront donc des sous-vues faisant partie de la vue principale qui est contrôlée par le ViewController et en suivant la chaine des répondeurs ils atterrissent sur lui. La documentation de NSView t'explique tout ça..
J'ai parcouru en vitesse le livre "Programmation Cocoa sous Mac OSX", et j'ai l'impression qu'il n'explique pas ce que je souhaite faire.
L'idée c'est de composer dynamiquement ma fenêtre avec de vues toutes préparées.
Imagine la fenêtre divisée en trois parties : une en haut, une au milieu, une en bas.
Je dispose d'une vue myForm
Je souhaite, lorsque je clique sur un bouton une vue myForm s'ajoute dans la partie du milieu, et qu'elle s'ajoute "en-dessous" des autres éléments de cette partie. En particulier, si je clique plusieurs fois sur le bouton, j'aurai plusieurs fois le formulaire (ce que je souhaite).
Voilà !
J'ai l'impression qu'il me manque pas mal de connaissances pour faire cela, comprendre comment les vues s'agencent dans une sur-vue, les systèmes de coordonnées, etc. et je suis à la recherche d'un tutoriel ou de ressources (livre, pdf, etc.) pour me former là -dessus !
Merci !
Regarde les View-Based Table Views
Ca devrait répondre à tes besoins.
Oui, je crois que ça correspond à ce que je veux faire !
Pour l'instant, je continue la lecture de mon livre, je reviendrai plus tard à mon projet.
Merci de votre aide à tous /smile.png' class='bbc_emoticon' alt=':)' />
PS :
Petite question annexe : si j'appelle 2 fois la vue que j'ai créée avec IB, j'aurais 2 fois le même objet ou bien j'aurai 2 instances différentes de la même view ?
à‰videmment, je souhaite avoir des objets différents, mais basés sur la même NSView.
PPS : Après cet échange, j'y vois plus clair sur les fichiers .xib et comment les intégrer dans l'architecture /smile.png' class='bbc_emoticon' alt=':)' />
Si j'ai bien compris ce que tu cherches à faire, ce dont tu as besoin est une NSTableView dans la partie centrale de ta fenêtre (puisque la partie en bas devra sans doute rester à l'écran, comme un "pied de page").
Ta NSTableView doit être view-based et non cell-based (le défaut). Dans ce cas, ta vue-formulaire s'ajoute en bas de la NSTableView, et le scrolling est automatiquement géré si tes formulaires dépassent la taille de cette NSTableView.
Le contrôleur de ta NSTableView sera un NSArrayController. Il pourra être contrôlé avec des boutons + et -, et tu le sous-classes pour implémenter ses méthodes add: (qui va charger une nouvelle vue, via ton NSViewController, et l'insérer) et remove:
Comme le fichier nib ne sert que de "patron" et qu'il est oublié après, tu obtiens une nouvelle instance de ton formulaire, donc un nouvel objet.
C'est comme ça que j'essaierais de faire si j'ai bien compris le but de ton application. Attention, les view-based NSTableView ne sont pas aussi évidentes que les cell-based. Regarde de ce côté:
http://developer.app...mmatically.html
Bonne chance!
Comme je n'avais pas le bon SDK, j'ai dû reprogrammer quelque chose de similaire ; pour l'instant, ça marche.
Mais, maintenant j'ai le bon SDK, donc je vais peut-être changer.
Je vais voir du côté de NSPopover aussi.
Merci !