Interagir avec un objet Cocoa par la programmation
Bonjour à tous,
Débutant sous Cocoa, je coince sur un problème d'accès aux objets d'une interface pourtant minimaliste...
Le contexte tient au fonctionnement d'une fenêtre de base incluant un NSTextField et un NSButton.
Après avoir créé les connections utiles IBOutlet et IBAction liant le code et les objets, je parviens à modifier le texte de mon NSTextField par un clic sur le NSButton ; mais ce que je souhaiterais faire en réalité c'est réaliser cette modification par programmation, en faisant appel à une méthode d'instance.
J'imagine que la problématique tient à envoyer le message (méthode d'instance) à l'objet NSTextField de la fenêtre affichée à l'écran, sur lequel je ne dispose d'aucun pointeur : c'est ce que j'aimerais savoir faire...
Merci de votre aide ;-)
déclaration : la classe MCNconnecteur est créée pour faire office de contrôleur
l'IBOutlet *texteChampTexte pointe sur le NSTextField
et l'IBAction mettreAjourParClic pointe sur le NSButton
implémentation
appel de la méthode
Réponses
Merci pour le retour rapide
J'ai modifié le code de AppDelegate selon ton conseil, mais pas plus de succès.
N'est-ce pas comme je le pense un souci d'adressage de message à la bonne instance de fenêtre ou/et de NSTextField ?
PS : Pour tout dire je tente de décortiquer et de reprendre le code d'un jeu d'échecs écrit en Objc, et comme je serais bien incapable de le traduire en qqchose de plus récent....
Mais je retiens la remarque taquine et sans doute motivée , pour un prochain projet.
En deux mots les avantages de Swift par rapport à Objc ?
Bien cordialement
Mon hypothèse est que la problème vient de l'endroit où tu instancies le MCNConnecteur.
Pour avoir une IBOutlet sur le textField, tu as dû être obligé d'ajouter une instance de MCNConnecteur dans ton xib/storyboard. Me trompe-je ?
Alors que quand le MCNConnecteur est instancié par l'AppDelegate, à aucun moment l'outlet texteChampTexte n'est fixée. Si tu regardes au débogueur, sa valeur doit être nil (0x0000000).
Il y a un autre problème. Quand tu instancies ton MCNConnecteur dans -[applicationDidFinishLaunching:], l'instance va être désallouée dès que le code sort de la méthode; forcément puisque c'est une variable locale. Tu dois la conserver en variable d'instance en déclarant une property.
Merci Céroce pour ta réponse et l'intérêt que tu portes à mon problème
Mis à part dans AppDelegate (qui présentait de mon point de vue de débutant, l'avantage d'être appelée à coup sûr par le programme), où verrais-tu avantageusement cette instanciation ?
Oui, c'est bien ce que j'ai fait
J'avoue ne pas savoir comment faire pour vérifier cette valeur.
J'ai bien posé un break point dans -[applicationDidFinishLaunching:] mais texteChampTexte n'est pas vraiment bavard...
Je crois comprendre l'idée de ce que tu diagnostiques là mais quant à le mettre en application...
J'ai tenté un "@property MCNconnecteur *monMCNConnecteur;" mais c'est peut-être une énorme co...rie !?
Accepté en compilation mais pas efficace...
Merci du temps que tu pourrais encore consacrer au noob que je suis
Bien cordialement
Bonjour à tous,
Des nouvelles, pour tous ceux que l'exercice pourrait également concerner...
À force de tâtonnements j'ai fini par résoudre le problème, et l'application-test fonctionne désormais comme attendu.
Le souci (trop bête, my bad) était dû à une mauvaise insertion dans le code des connexions avec l'interface.
Je retiendrai pour l'avenir à titre tout à fait personnel, qu'une organisation fonctionnelle doit répondre à deux principes simples :
1. Rassembler toutes les connexions avec l'interface (IBOutlet /IBAction) dans une classe unique ayant vocation de contrôleur
2. Positionner un IBOutlet d'une instance de cette 'classe contrôleur' dans chaque entête de classe ayant besoin d'un accès aux objets de la vue
Le code corrigé et nettoyé de l'appli est en PJ pour info, sachant que l'interface a cette tête :
Bien cordialement
Comme tu as l'air d'avoir du background avec d'autres langages, je pense que le concept du MVC ne t'es pas étranger.
Ce qui faut comprendre est que les outlets et les actions se tirent normalement vers un contrôleur; typiquement un NSWindowController (ou un NSViewController si tu veux découper ta fenêtres en sous-vues, si elles doivent être réutilisées ou que la fenêtre est complexe).
Celui-ci peut être instancié dans le xib ou storyboard.
Pour le modèle, tu n'en n'as pas défini dans ton exemple. Ça peut être un simple héritier de NSObject.
Ça amène une question: où instancier l'objet racine du Modèle ?
Dans les deux cas, il faudra déclarer une propriété pour conserver l'objet.
En théorie, un NSObject peut faire office de connecteur, mais en pratique, il faut utiliser un NSWindowController.
Il faut comprendre un minimum comment ça marche: un objet va instancier un NSWindowController qui va charger un xib/storyboard. Ce fichier dit: je comporte tels objets avec telles propriétés. J'ai des outlets qui pointent vers File's Owner (ou vers un objet interne au xib/storyboard).
Qui est File's Owner ? L'objet qui instancie le xib/storyboard, donc le NSWindowController.
Ainsi, disons que le NSTextField est instancié à partir du xib, et que l'outlet est appelée nameTextField, ça va copier la valeur du pointeur sur le NSTextField dans la propriété nameTextField du NSWindowController.
Ça utilise un mécanisme qu'on appelle le «Key-Value coding», qui permet de fixer une propriété dynamiquement en utilisant son nom. D'ailleurs tu vas bientôt faire une erreur à mélanger les outlets, et tu auras un message dans la console du type «Key error: Undefined key 'nameTextField'»
Alors tu avais deux instances différentes de MCNConnecteur:
Le problème était que les outlets pointaient sur celle du xib, donc forcément, les outlets de MCNConnecteur n'étaient pas fixées (= nil = 0x00000000) et rien ne se passait.
Appeler une méthode sur nil est légal en ObjC. Ça ne fait rien, mais c'est permis. (mais pas en Swift!)
C'est parce qu'il est défini en variable locale. Dès que le code sort de la portée, il est désalloué.
Si, si, c'est bien ainsi, qu'on déclare une propriété (~= une variable d'instance). Mais je t'ai expliqué plus haut pourquoi ça ne pouvait pas fonctionner (pas la bonne instance).
Merci Céroce pour toutes ces précisions que je m'efforce de comprendre au mieux
Je crains cependant d'abuser avec de nouvelles demandes de précisions qui vont sans doute te sembler des évidences...
Je prends bonne note de tout ça
Ne l'est-il pas forcément ? Car pour tirer des outlets vers le code du contrôleur, une 'instance' de ce contrôleur doit être présente graphiquement dans le XIB, non ?
J'en prends bonne note également
Je comprends, dans mon cas d'une appli sans document, que je dois instancier un "ModeleNSObject" dans l'AppDelegate et déclarer dans ce même AppDelegate qqchose comme "@property ModeleNSObject *monModeleNSObject;"
C'est bien ça ?
Oops
Là j'avoue ça va trop vite pour moi...
Toujours dans mon cas de figure, quand tu dis 'un objet va instancier...' ça signifie que dans la classe du Modèle je dois instancier un Contrôleur qui va donner accès aux objets de la Vue puisqu'il a tous les outlets nécessaires ?
Du genre, pour le code de AppDelegate.m :
import "AppDelegate.h"
@interface AppDelegate ()
@property (strong) IBOutlet NSWindow *window;
@property MCNmodele *monMCNmodele;
@end
@implementation AppDelegate
@synthesize monMCNmodele;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
MCNmodele *monMCNmodele = [[MCNmodele alloc] init];
[monMCNmodele modifParCode];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
@end
Mieux qu'un 'corrigé, tu aurais un exemple de code ?
Oops Oops
Je vais dormir là dessus et m'y repencher un peu plus tard...
C'est donc une de trop si je comprends, et celle sur laquelle il faut travailler est uniquement celle du XIB...
Mais comment invoquer dans le code précisément cette instance là ?
Et cette déclaration est à faire dans la classe en question, ou bien dans chacune de celles souhaitant accéder à la propriété
Désolé d'avoir été long, et sans doute un peu lourd.
Merci encore !
Bien cordialement.