View : accéder à  son controleur ?

BrindavoineBrindavoine Membre
14:57 modifié dans API UIKit #1
Bonjour je bloque sur un probleme de débutant depuis plusieurs heures !!

Mon probleme par l'exemple :

Je crée un nouveau projet de type View Based Application.
J'ai donc de créé un MyAppDelegate et Un MyViewController
J'ai aussi un MainWindow.xib ainsi qu'un MyViewController.xib

Je rajoute simplement une classe custom pour ma view "CustomView" que le lie à  la view dans interface builder de MyViewContoller.xib

Mon probleme : j'aimerais créé une propriété dans ma view qui pointe faire mon controler car j'aimerais m'en servir pour certaines actions comme delegate...

J'ai donc mis :

#import &lt;UIKit/UIKit.h&gt;<br /><br />@interface CustomView : UIView {<br />	// Delegate<br />	IBOutlet id delegate;<br />}<br /><br />@property (assign) id delegate;<br /><br />@end


Comment linker la propriété delegate sur l'instance du controller??
Le controller étant le file Owner, j'ai essayé de mettre delegate ---> File's Owner en vain.

J'ai tenté aussi de créer dans IB un "object" de type MyViewController et de faire pointer l'outlet delegate vers celui ci, ca ne marche pas non plus...

Comment faire ?



Réponses

  • Philippe49Philippe49 Membre
    14:57 modifié #2
    Ne voyant pas de problème, je ne donnerais pas de réponse  :)  ;)
    Ah si, le terme delegate est ici mal choisi, car la fonction d'un delegate est bien précise en Obj C.
  • GreensourceGreensource Membre
    14:57 modifié #3
    idem je n'utiliserais pas un delegate pour ça.
    Par contre pour ton souci, tu as juste a ajouter un attribut IBOutlet MyViewController* et faire le lien avec IB, ya pas de raison que ça coince.
  • BrindavoineBrindavoine Membre
    14:57 modifié #4
    Bon, en effet, ca passe bien dans ton exemple Philippe !

    Mon programme est en fait plus complexe et c'est sûrement de là  d'ou viens l'erreur :

    Je me sert en fait de ma CustomView comme classe abstraite qui sert de base à  deux vues : la vue portrait et la vue landscape qui ne font que surcharger les méthodes de dessin... (car j'ai deux affichages possibles, cf mon post de validation d'architecture)

    donc ce n'est pas directement CustomView que je lie dans IB mais PortraitView (qui est appelée par défaut. (je ne switche pas encore entre les deux, ce sera sûrement pour une prochaine question...)

    Quand je place un NSLog(@controller : %@,delegate); dans la méthode drawRect,  de PortraitView c'est impeccable, mais lors de l'init qui est géré par la classe abstraite customView, j'ai toujours null"...

    Et j'ai justement besoin de mon controller lors de l'init car c'est lui qui fait lien avec mon model...
  • yoannyoann Membre
    14:57 modifié #5
    Une chose plus simple que tu peut faire c'est mettre ton ViewController en property de ton AppDeleagte et tu l'appel comme ça :

    [UIApplication sharedInstance].delegate.myViewController

    De cet manière tu y a accès de partout et sans avoir à  refaire le lien vers le delegat quand tu va changer ta vue entre les deux passages.
  • BrindavoineBrindavoine Membre
    14:57 modifié #6
    J'ai trouvé...

    Le probleme proviens de l'odre d'exécution. J'essayais d'obtenir le controller depuis l'initialisation, ors celui ci n'était pas encore défini...

    si j'ajoute à  l'exemple de Philipe :
    - (id)initWithCoder:(NSCoder *)coder
    {
        if (self = [super initWithCoder:coder]) {
    NSLog(@controller depuis coder : %@",controller);
        }
        return self;
    }

    Cela ne fonctionne pas...

    Bon, ben je vais simplement organiser mon code autrement.

    Merci en tt cas
  • AliGatorAliGator Membre, Modérateur
    14:57 modifié #7
    Au risque de faire de la répétition (cherche sur les présents forums pour plus d'explications), lors du chargement d'un NIB, l'ordre est en gros ainsi :
    - chaque instance que tu as dans ton XIB est créée et initialisée, donc reçoit un appel à  init... (initWithCoder / initWithFrame / initWithNibName:bundle: / init ... selon le type d'objet et le contexte, voir autres sujets sur le forum pour plus d'infos)
    - une fois que toutes les instances sont créées et initialisées, les connections (IBOutlets) sont réalisées. Il ne peut les faire qu'après, bien évidemment, puisque tu peux très bien avoir dans l'objet A un IBOutlet vers l'objet B, mais si l'objet B n'est pas encore alloué et initialisé au moment de la création de l'objet A, il aura du mal à  faire la connexion...
    - Une fois que tout est fini et le NIB chargé, une méthode est appellée à  la fin de tout ça. Sous Mac c'est awakeFromNib, sous iPhone on utilise plus souvent viewDidLoad pour les initialisations post-chargement de NIB.

    C'est pour ça que ces IBOutlets ne sont pas encore connectés (donc valent nil) dans le init, ils ne sont pas encore "prêts".
  • GreensourceGreensource Membre
    14:57 modifié #8
    Ce que tu dis me fait penser à  un truc. Plusieurs fois je me suis retrouver dans cette position mais sans utiliser IB. A savoir, devoir initialiser des objets qui avait des liaisons l'un vers l'autre.
    Je constate qu'Apple fait donc:
    Une initialisation de base et ensuite seulement les liens. Est-ce propre? N'y a t'il pas un risque "d'oublier" de faire les liens?
  • yoannyoann Membre
    14:57 modifié #9
    dans 1247777369:

    Ce que tu dis me fait penser à  un truc. Plusieurs fois je me suis retrouver dans cette position mais sans utiliser IB. A savoir, devoir initialiser des objets qui avait des liaisons l'un vers l'autre.
    Je constate qu'Apple fait donc:
    Une initialisation de base et ensuite seulement les liens. Est-ce propre? N'y a t'il pas un risque "d'oublier" de faire les liens?


    En soit comment veux tu pouvoir en oublier ? Lorsque le xib est chargé, toute les instances sont créé puis les valeurs sont assigné et les "lien" fait.

    Quand tu le fait en code c'est la même chose, tu crée ton objet, le règle, le place dans ta vue et garde une référence dessus pour éventuellement la pacer à  un autre objet.


    Qu'est-ce qui te chagrine dans ce process ?
  • GreensourceGreensource Membre
    14:57 modifié #10
    Ce qui me chagrine c'est que ça donne:
    MaClass* monObjet = [MaClass alloc] init];<br />[monObjet setUnTruc];
    


    Au lieu de:
    MaClass* monObjet = [MaClass alloc] initWithUnTruc:monTruc];
    


    C'est pas le fait de rajouter une ligne n'y le risque d'oublier la deuxième en fait, c'est juste que j'ai l'impression que l'architecture de mes classes n'est pas bonne lorsque je suis obliger de faire ça.
  • AliGatorAliGator Membre, Modérateur
    14:57 modifié #11
    Oui sauf que si monTruc n'existe pas...
    Imagine exemple type, que tu aies besoin de faire des références croisées (finalement c'est courrant du moment qu'il n'y a pas de retain loop, donc qu'un des 2 soit assign et pas retian), comme un ViewController A qui possède une View B, qui elle-même a une variable "delegate" pointant en fait sur A (exemple type d'ailleurs).

    Tu fais comment avec ta solution ? Tu crées A en lui passant B directement... ah mince B n'existe pas encore. Bon tu crées B avant alors, puis tu crées A en lui passant B. Au oui mais mince quand tu crées B faut lui passer A... oups :P

    MaClass* monObjet = [[MaClass alloc] init];<br />MaClass2* monTruc = [[MaClass2 alloc] init];<br />[monObjet setTruc:monTruc];<br />[monTruc setDelegate:monObjet];
    


    De toute façon vu que : (1) ce genre de cas peut arriver et (2) Lorsque tu désarchives un XIB il ne peut pas savoir les méthodes non-standard (genre initWithUnTruc) qui sont disponibles pour initialiser ton objet directement en passant des paramètres, bah le désarchivage du XIB ne peut pas s'y prendre autrement que d'abord faire des init/initWithCoder/initWithFrame (qui sont des méthodes standard, elles), et ensuite affecter les variables (IBOutlets).
  • GreensourceGreensource Membre
    14:57 modifié #12
    Bas ouais c'est aussi ce que je me disais, je voyais pas comment faire autrement. Je devais avoir besoin d'être rassurer voilà  tout  :o

    Merki
  • bnkbnk Membre
    14:57 modifié #13
    Salut,

    Après avoir lu ce post j'ai voulu revoir un peu mon organisation Controller/Views pour faire quelque chose de plus propre.

    Ceci semble fonctionner sauf que ma View chargée par le controller ne reprend pas les éléments que j'ai placé à  la main dans IB.

    Voici mon code et mes liens IB:

    <br />APPCLASSIQUECONTROLLER.H<br /><br />#import &lt;UIKit/UIKit.h&gt;<br />#import &lt;Foundation/Foundation.h&gt;<br />#import &quot;appClassiqueViewS.h&quot;<br /><br />@interface appClassiqueController : UIViewController {<br />    IBOutlet appClassiqueViewS *appClassiqueSujet;<br />}<br /><br />@end<br /><br />*********<br /><br />APPCLASSIQUECONTROLLER.M<br /><br />#import &quot;appClassiqueController.h&quot;<br /><br />@implementation appClassiqueController<br /><br /><br /> // Implement loadView to create a view hierarchy programmatically, without using a nib.<br /> - (void)loadView {<br />	 //viewSujet = [[appClassiqueViewS alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)];<br />	 <br />	 appClassiqueSujet = [[appClassiqueViewS alloc] init];;<br />	 [self setView:appClassiqueSujet];<br /> }<br />...<br />@end<br />
    


    <br />APPCLASSIQUEVIEWS.h<br />#import &lt;UIKit/UIKit.h&gt;<br />#import &lt;Foundation/Foundation.h&gt;<br /><br />@interface appClassiqueViewS : UIView {<br />    IBOutlet UIButton *btn_lancervideosujet;<br />    IBOutlet UITextView *tvsujet;<br />}<br />- (IBAction)actionlancervideosujet:(id)sender;<br />@property (nonatomic, retain)UITextView *tvsujet;<br />@end<br /><br />*********<br />APPCLASSIQUEVIEWS.M<br /><br />#import &quot;appClassiqueViewS.h&quot;<br /><br />@implementation appClassiqueViewS<br />@synthesize tvsujet;<br /><br />- (IBAction)actionlancervideosujet:(id)sender {<br />}<br /><br />- (id)initWithFrame:(CGRect)frame {<br />    if (self = [super initWithFrame:frame]) {<br />        // Initialization code<br />		NSLog(@&quot;init view Sujet&quot;);<br />		[self setBackgroundColor:[UIColor redColor]];<br />    }<br />    return self;<br />}<br /><br /><br />- (void)drawRect:(CGRect)rect {<br />    // Drawing code<br />	NSLog(@&quot;init view Sujet3&quot;);<br />}<br /><br /><br />- (void)dealloc {<br />    [super dealloc];<br />}<br /><br /><br />@end<br />
    


    Quand je charge mon controller j'obtiens bien:
    2009-07-22 16:48:23.724 myPocketApp[14107:20b] init view Sujet
    2009-07-22 16:48:23.726 myPocketApp[14107:20b] init view Sujet3

    Voici comment tout ceci est relié sous IB:

    UIViewController: APPCLASSIQUECONTROLLER
    a648396fc7e8941ac60f471cf66b9.png

    UIView : APPCLASSIQUEVIEWS
    28e91d50479b8507bd866d4d1014a.png

    Résultat dans mon simulateur: mon UIView APPCLASSIQUEVIEWS est bien affichée mais les éléments que j'ai ajouté sous IB (le textView et le bouton) n'apparaissent pas en revanche le background est bien rouge.

    C'est la première fois que j'essaie d'organiser mon programme de cette manière. Qu'ai-je oublié?
    merci

  • GreensourceGreensource Membre
    14:57 modifié #14
    C'est parce que tu créer une nouvelle vue dans loadView du Controller. Toi ce que tu veut c'est récupérer la vue que tu as décrite dans IB. Et cette vue là  c'est l'attribut view de ton controller. [self view] pour le chopper, et normalement ça sera bon.

    Toi ce que tu as fait, c'est remettre une deuxième vue par dessus, du coup elle cache la bonne  ;)
  • bnkbnk Membre
    juillet 2009 modifié #15
    Merci pour ta réponse Greensource.

    En fait oui je la remet par dessus car je ne comprennais pas pourquoi elle n'était pas appelée direct au chargement de mon controller alors que j'avait relié le View de mon controller sous IB à  appClassiqueViewS (elle n'était pas appellée car j'avait aucun log en console et un écran blanc au lieu de rouge).

    Ceci est corrigé en ajoutant :
    <br /> - (void)viewDidLoad {<br /> [super viewDidLoad];<br />}<br />
    


    Du coup si je veux chopper mes autres vues crées par IB je devrais pouvoir le faire avec ce que tu as dit: [self view].

    merci pour l'aide
    a+
  • GreensourceGreensource Membre
    14:57 modifié #16
    Exactement tu fais [self.view subviews] et tu les as toutes!
  • bnkbnk Membre
    juillet 2009 modifié #17
    dans 1247725427:

    Par contre pour ton souci, tu as juste a ajouter un attribut IBOutlet MyViewController* et faire le lien avec IB, ya pas de raison que ça coince.


    question supprimée car j'ai trouvé la solution

    Ca coincait chez moi jusqu'à  ce que je mettre des  #import "moncontroller.h" @class moncontroller dans le header de ma vue et  #import "mavue.h" @class mavue dans le header de mon controleur.

    Quelqu'un peut m'expliquer à  quoi sert de @class ?
    #import sert à  importer les méthodes et attributs d'une classe externe mais le @class?


    EDIT2: question déjà  posée ici:

    http://www.osx-dev.com/index.php?topic=3308.0
  • GreensourceGreensource Membre
    juillet 2009 modifié #18
    Déjà  en effet tu n'as pas besoin dans ton cas de faire un #import dans les .h
    Le #import est "brutal" il importe tout le code associé. Alors que le @class sert juste à  dire au compilo, "t'inquiète la classe existe bien, il a le droit de s'en servir" mais au final il ne sait pas ce qu'il y a dedans, au contraire d'#import.

    Sinon à  propos de ton code, peut être que je me trompe, mais ta vue ne voudrais tu pas plutôt l'associé à  l'attribut "view" de ton controller, plutôt que de créer un autre attribut?

    [edit] tu es réactif dit donc ^^, c'est vrai que je me rappelait avoir déjà  posé la question ;)
  • GreensourceGreensource Membre
    14:57 modifié #19
    dans 1247777369:

    Ce que tu dis me fait penser à  un truc. Plusieurs fois je me suis retrouver dans cette position mais sans utiliser IB. A savoir, devoir initialiser des objets qui avait des liaisons l'un vers l'autre.
    Je constate qu'Apple fait donc:
    Une initialisation de base et ensuite seulement les liens. Est-ce propre? N'y a t'il pas un risque "d'oublier" de faire les liens?


    J'ai une autre question à  ce propos. Si on se retrouve à  faire l'initialisation est deux fois donc et que la deuxième partie se trouve pas mal après. Ca ne risque pas de créer un souci avec  le dealloc? Ne peut-on pas se retrouver à  désallouer quelques qui n'existe pas encore?
    La solution que je verrais c'est de créer un faux objet quand même à  l'initialisation. Ou bien d'avoir une grande confiance dans son code ;-)
  • AliGatorAliGator Membre, Modérateur
    14:57 modifié #20
    J'ai rien compris :)
  • GreensourceGreensource Membre
    juillet 2009 modifié #21
    :) Je me disait bien que je n'était pas clair.
    En gros si je fait:
    MaClass* monObjet = [[MaClass alloc] initWithCertainTrucs:desTrucs];<br />...<br />des choses se passes<br />...<br />[monObjet setUnTrucNecessaireMaisPasDispoAvant:unTruc];<br />...
    


    Or dans le dealloc de MaClass, je release le truc nécessaire mais pas dispo avant. Ya pas un risque que le dealloc soit appelé trop tôt (avant le setUnTrucNecessaire...) et que du coup j'ai un release sur quelques choses qui n'existe pas.

    Purée je suis désoler mais j'ai du mal à  exprimer mon truc.
  • AliGatorAliGator Membre, Modérateur
    14:57 modifié #22
    Heu je capte pas sur le "plus tard" tu entends dans la même méthode ? Car ton "dealloc" ne peut pas être appelé avant que la méthode (celle où tu fais ton initWithUnTruc puis ton setUnAutreTruc) se soit terminée, donc je vois pas trop ton use case ?

    Et si c'est à  2 endroits séparés, bah si ton unTruc n'était pas créé avant, il va valoir nil, donc l'envoi du message dealloc à  nil n'aura aucun effet.

    Bref j'ai du mal soit à  voir le use case auquel tu penses, soit à  voir comment il peut arriver.
  • GreensourceGreensource Membre
    14:57 modifié #23
    Oui oui, c'est plus tard dans une autre méthode. Mais je ne savais pas que ça valais nil. C'est juste quand on a fait release sur quelques chose qui a un retain count de 1 qu'on ne peut plus le faire après. Ca signifie que la variable ne prendre pas nil comme valeur après un release?
    donc var = nil et [var release] c'est très différent.
  • zoczoc Membre
    14:57 modifié #24
    dans 1248466574:
    Ca signifie que la variable ne prendre pas nil comme valeur après un release?
    donc var = nil et [var release] c'est très différent.
    C (et par conséquent Objective-C) est un langage de bas niveau. Par conséquent il ne fait rien "dans le dos" du développeur. Donc non, release ne mettra jamais une ivar à  nil.

    Et oui, var = nil et [var release] sont très différents, le premier utilisé seul aboutissant à  une fuite de mémoire généralement, sauf quand on programme pour Leopard avec le garbage collector activé.
Connectez-vous ou Inscrivez-vous pour répondre.