Cocoa avec un nib minimal
Philippe49
Membre
dans 1210255384:
Il y aune chose que je ne comprends pas : pourquoi apple interdit - de fait - de construire des GUI sans IB sous cocoa ?
Y a-t-il des secrets industriels, mais alors que Apple le dise clairement ?
Moi je suis prêt, comme tout vieux programmeur, à aligner du code, mais encore faut-il avoir les clés.
J'ai compris, ou je crois avoir compris, comment IBAction fonctionne et comment on peut se passer de IB. Mais je n'arrive pas à comprendre comment IBOutlet fonctionne, et donc pas comment m'en passer.
Dans mes longues recherches, je me rends compte que je ne suis pas le seul à me poser ce genre de question. Je vois aussi qu'il n'y a pas vraiment pas d'autre réponse que : "vous n'avez qu'à utiliser IB"...
IB rend "opaque" ce qui serait simple pour n'importe qui ayant bidouillé des fenêtres et des évènements.
Moi je suis prêt à animer un groupe : OC avec cocoa sans IB ?
Toujours merci
Ci-joint une application qui utilise IB au minimum :
Pas de fenêtres dans MainMenu.nib,
Un contrôleur qui se chargera de créer à la main les différentes fenêtres et leur contenu
Une référence à l'instance de NSApplication
Une référence au menu principal
Pas d'IBAction, pas d'IBOutlet, on peut ensuite coder tout ce que l'on veut en oubliant complètement IB.
Ceci dit, IBAction est synonyme de void, et au point de vue programmation, ce n'est rien.
IBAction et IBOutlet servent lorsqu'on fait son programme pour synchroniser correctement les deux applications distinctes que sont XCode et Interface Builder : Interface Builder parcourt les fichiers du projet afin de pouvoir réagir en phase avec le projet (Connections à réaliser, détections des connections non faites ou fausses, ...)
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
/*
Compiler : gcc pgm.m -o pgm -framework Cocoa -Wall
*/
#import <Cocoa/Cocoa.h>
@interface AppController : NSObject {
NSMutableArray * windows;
}
- (void)awakeFromQuedal;
@end
@implementation AppController
- (void)awakeFromQuedal
{
windows=[[NSMutableArray alloc] init];
int i;
for(i=0;i<5;i++){
NSRect frameWindow=NSMakeRect((i+1)*10.,50.+20*i,400.,400.);
NSWindow* window=[[NSWindow alloc] initWithContentRect:frameWindow
styleMask:
NSTexturedBackgroundWindowMask | NSTitledWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:NO
screen:[NSScreen mainScreen]];
[window setBackgroundColor:[NSColor windowBackgroundColor]];
[window setTitle:[NSString stringWithFormat:@window %d,i]];
NSButton * button=[[NSButton alloc] initWithFrame:NSMakeRect(20.,20.,100.,20.)];
[[window contentView] addSubview:button];
[window makeKeyAndOrderFront:self];
[window setDelegate:self];
[windows addObject:window];
[window release];
}
}
-(void) dealloc {
[windows release];
[super dealloc];
}
@end
int main(int argc, char**argv){
[NSApplication sharedApplication];
AppController * controller=[[AppController alloc] init];
[controller awakeFromQuedal];
[NSApp run];
}
Au fait, en parlant de doc, y a-t-il un endroit où trouver tout cela ou faut-il aller chercher dans des milliers d'endroits ?
Première question (et il y en aura d'autres, c'est le risque de fournir une réponse pertinente !) :
peut-on passer des paramètres dans un selector ? ex:
[closeButton setAction:@selector(closeWindow::i)];
J'ai l'impression que non, mais ?
Pour faire court : oui. Mais attention à éviter la confusion :
Un selector permet de stocker une référence vers une méthode. Si on dispose d'un objet et d'un selector, on peut donc déclencher la méthode correspondante de cet objet.
On a alors
Si la méthode à des arguments, la famille des "performSelector..." permet de gérer les cas les plus simples (1 ou 2 arguments de type objet)
Quand les arguments sont plus nombreux et ne sont pas de type objet, il faut se tourner vers NSInvocation.
Maintenant, une "action", ça n'est pas un type particulier. C'est simplement comme ça que l'on appelle une méthode susceptible d'être déclenchée par un événement simple d'interface. Une telle méthode ne renvoie rien (void) et n'a qu'un paramètre de type objet (le déclencheur). Si je crée une telle méthode, je peux alors l'attribuer à un contrôle de l'interface comme étant son "action".
Exemple :
Les "actions" permettent de gérer les événements les plus simples de l'interface (clic, changement de valeur d'un slider, validation d'un textfield...)
Pour des événements plus complexes (drag'n drop...), on utilise d'autres techniques (par exemple en surchargeant des méthodes venant de NSResponder).
+
Chacha
Le premier exemple est à mon avis le moins qu'on puisse prendre de l'architecture Cocoa (sinon il y a beaucoup de choses toutes faites à refaire ou vérifier à la main (les menus, et je ne sais trop quoi qui vient automatiquement dans le Kit ..)
Là il n'y pas besoin de doc. Ce sont les méthodes classiques qui sont utilisées NSWindow, NSControl, ...
Tu vas dans la doc de NSWindow par exemple, et tu as dans l'encadré du haut, ou dans le TOC, la référence à une Doc "Companion Guide" qui est ici "Window Programming Guide"
Pour le second exemple, la première référence à suivre est celle de NSApplication : Application Architecture Overview.
damned  >:D
Là encore il te faut la doc de setAction concernant les NSButton
1) tu vas dans la doc sur NSButton  Menu Help > Documentation  et NSButton dans searchField
Tu y vois que NSButton hérite de NSControl, qui lui-même hérite de NSView, de NSResponder et enfin de NSObject. Cela signifie que la méthode setAction qui t'intéresses se situe dans l'une de ces classes.
2) Â tu mets setAction dans le searchField, et tu sélectionnes la méthode de NSControl
et là tu arrives au prototype de la méthode qui te donnes la réponse à ta question :
- (void)setAction:(SEL)aSelector
Pour voir comment utiliser un selector, tu mets selector dans le searchField et tu règles sur FullText.
Par contre closeWindow ne semble pas exister. Il faut donc chercher dans NSWindow la méthode de fermeture. Et tant qu'à faire dans NSApplication la méthode pour quitter proprement l'appli.
Elle est pas belle la vie ? Â :brule:
PS : Ah oui, faut pas oublier d'affecter la cible à ton bouton, via la méthode  setTarget:
Non.
Je rajouterais au bel exposé de Chacha sur la famille des performSelector une méthode des collections que j'ai aperçue récemment qui me semble nouvelle (?)
- (void)makeObjectsPerformSelector:(SEL)aSelector
Oh, c'est pas neuf ! Il est bien stipulé en dessous :
Mais c'est une bien bonne idée de la citer.
Je me débrouille assez bien avec ce code.
Je constate que NSApplication génére un élément de menu dans la barre d'application mac en haut de l'écran.
Comment construire un menu d'application complet dans la logique TSIB ?
J'ai essayé des NSMenu, des NSMenuItem, mais la clé me manque.
Merci
...
int main(int argc, char**argv){
[NSApplication sharedApplication];
AppController * controller=[[AppController alloc] init];
[controller awakeFromQuedal];
[NSApp run];
}
[/quote]
Tu as rajouté quelque chose au code ?
Dans mon essai, c'est le terminal qui définit la barre de menu ?
Tu peux complètement définir ad nihilum la barre de menu d'une appli par programme (donc sans MainMenu.nib).
Il y a 1 contrainte : le menu application n'est pas définissable : il est automatiquement créé.
Ca marche à peu près : le menu fichier apparaà®t bien avec l'item quitter
sauf
ClearMenuBar();
qui semble inconnu dans cocoa, ou obsolète ?
J'ai trouvé des choses mais liées à carbon apparemment ?
PS: je croyais que le pool était géré automatiquement et qu'il n'était pas nécessaire de l'expliciter ?
Tu peux complètement définir ad nihilum la barre de menu d'une appli par programme (donc sans MainMenu.nib).
Il y a 1 contrainte : le menu application n'est pas définissable : il est automatiquement créé.
[/quote]
C'est une fonction Carbon en effet qui appartient au Menu manager de carbon.
Les menus sous cocoa font appel au Menu Manager en interne.
Donc, c'est parfois utile d'appeler directement les fonctions du Menu Manager sans passer par la couche cocoa (qui est incomplète).
Il est créé et détruit automatiquement en début puis fin boucle de gestion des événements (donc toutes les méthodes appelées à partir d'une action de l'utilisateur dans l'interface ont un releasePool à disposition).
Ici, dans le main, il n'existe pas donc il faut le créer puis le détruire sois même.
peux-t-on appeler ClearMenuBar() ?
J'ai introduit le #import <Carbon/Carbon.h> et le #import <.../Menus.h>
Le build est ok,
mais à l'exécution le symbole n'est pas défini.
Il doit manquer qq chose, mais quoi ?
Il faut importer le framework Carbon
C'est évident, j'y ai pensé cette nuit et c'est fait.
Mais je ne vois pas à quoi sert ClearMenuBar, puisqu'il y a toujours le nom de l'appli, avec quelques items sous-menu, notamment "quit".
N'est-il pas possible de gérer ça totalement par programme, en supprimant les choix par défauts, ClearMenuBar semble ne supprimer rien ?
Il y a de fortes chances pour que le ClearMenuBar() soit exécuté mais qu'un automatisme Cocoa repasse par derrière pour installer la présentation standard des applications.
Je crois que pour prendre vraiment la main comme tu veux le faire, il faut programmer sous Carbon pour déconnecter tous les automatismes que Cocoa met en place .. pour nous faciliter la vie.
C'est très fréquent de mélanger Carbon et Cocoa. C'est permis et même parfois recommandé par Apple.
Pour les menus, Cocoa n'offre aucun gestionnaire. En fait, Cocoa repose entièrement sur le Menu Manager de Carbon. Toutes les méthodes de NSMenu et NSMenuItem ne sont que des appels déguisés à des fonctions Carbon.
Dans Google, il suffit de taper en recherche "NSMenu Carbon" pour s'en rendre compte.
ClearMenuBar() est utilisé ici pour finir d'initialiser la barre de menu, c'est à dire de mettre le menu pomme et le menu application dans un état correct, ce qui laisse le reste de la barre des menus libre pour être rempli par programme.
Oui et non : en effet tout faire à partir de Menu Manager de Carbon est peut-être la meilleure chose à faire. Mais après ça devient compliqué de relier les actions utilisateur faites dans ces menus avec le reste du programme cocoa, à moins de refaire le même glue-code qu'a fait Apple pour lier NSMenu au Menu Manager. Mais si c'est pour refaire ce qui existe déjà ... Où est le gain ?
Le TSIB, c'est pas bien. Quitte à faire le minimal, faire un MainMenu.nib avec juste un embryon de barre de menu. Tout le reste c'est de la bidouille qui risque de ne pas être stable dans les futures versions de l'OS.
Autant pour moi : Carbon-Cocoa integration Guide , mais ce n'est pas recommandé pour les UI.
Que sait-on de ce que fait exactement  [NSApp setMainMenu:barre_menu]; comme appels ?
C'est là que le mélange des API devient opaque et ambigu. Il vaut mieux prendre le parti de choisir une API et de s'y tenir.
Objective-C is a superset of ANSI C, so calling Carbon functions from a Cocoa application is easy as long as they are not user interface functions. A Cocoa application can always call low-level Carbon functions because Cocoa already links against the Application Services framework.
Interface Carbon dans du Cocoa
C'est ce que je dis depuis le début, mais notre ami semble curieux, alors ...
C'est exact.
Le truc du ClearMenuBar() est une astuse (un trick) qui a été dévoilé sur une des dev-list d'Apple. Mais rien ne garantit qu'à l'avenir ce sera toujours le cas.
Une application Cocoa exige un NSPrincipalClass et un NSMainNibFile dans son info.plist.
Respectons ce choix.
Ca ne sert à rien de renvoyer vers des liens qui ne répondent présentement pas au sujet.
Ce que dit le document Apple, c'est comment intégrer des contrôles Carbon dans un environnement Cocoa. Mais rien n'explique par la suite comment interagir avec.
Respectons la convivialité ...
Pour ma lecture, c'était bien dans la discussion de mélanger Carbon et Cocoa dans la gestion de l'interface. Cette page ne donne certes pas de réponse mais elle montre les précautions que prend Apple sur ce sujet, confirmant ainsi sa réticence sur le mélange des UI .
Je suis un esprit pervers qui aime comprendre ce qu'il fait. IB ne le permet pas et les fichiers .nib (terminal, c'est du xml non utilisable) non plus.
J'ai fais des tas d'essai (comment, uncomment) à partir d'une base cocoa application minimale avec xcode :
ClearMenuBar ne semble servir à rien.
Dans tous les cas, il y a une barre de menu d'application avec au minimum le menu ayant le nom de l'application (NSBundle comment) .
Sinon, on retrouve le menu traditionnel et la fenêtre de base. Avec IB, on peut supprimer le mainMenu et la mainWindow, mais dans la barre de l'application, il y a toujours le menu nom de l'application.
QUESTION : comment faire référence au menu minimal, toujours créé, par exemple pour changer le titre et ajouter des sous menus par programme (dans le main ou ailleurs) ?
PS : c'est inquiétant ce que tu dis sur la stabilité dans les futurs OS!?...
Aucune perversité là dedans, j'avoue plutôt aimer le questionnement.
Cependant, quoiqu'on fasse en Info, on est en pelure d'oignon : un empilement de couches superposées, et dire qu'on explique la Couche A par sa sous-couche B c'est relativement illusoire. Même l'assembleur est en lui-même une couche ...
Cocoa nous en donne l'accès par MainMenu.nib, ce qui n'est que remplacer du code écrit par une action graphisme, transcrite par ailleurs en fichier ...
Non, la politique Apple est claire : soit c'est documenté, et Apple s'engage soit à assurer la mise à jour, soit à indiquer (en général sur 2-3 ans) que les méthodes sont marquées comme Deprecated.
Cocoa nous en donne l'accès par MainMenu.nib, ce qui n'est que remplacer du code écrit par une action graphisme, transcrite par ailleurs en fichier ...
Bon d'accord. Mais je veux construire quarante mille applications avec des menus changeant à chaque application, liés, par exemple, au numéro de l'application. Avec IB je suis sûr de me tromper des milliers de fois, alors qu'en programmant je n'aurai aucun problème.
IB ne permet pas la construction d'applications robustes, ni reproductibles : tout ce qui se fait "à la main" (comme avec IB) est générateur systématique d'erreurs, alors que ce qui est programmé, une fois le code propre (ce qui est un autre problème), supprime ce genre d'erreurs.
Donc re : ma QUESTION
Tu fais un template perso dans Xcode : Psychoh13 a fait un post sur ce sujet.
Dans ce template,
tu mets le MainMenu.nib à son minimum (effacer/remplacer les menus prédéfinis)
tu mets une classe d'initialisation de l'interface qui vient se mettre dans chaque projet ouvert selon ce template.