DrawRect & initWithFrame

CeetixCeetix Membre
06:39 modifié dans API AppKit #1
Bonsoir tout le monde.

Voilà  j'ai une petite question et un problème.

Je voulais savoir si quand on appelle setNeedsDisplay la méthode initWithFrame est aussi rappelée?
Pour moi non, c'est juste drawRect qui est appellée mais je ne suis pas sûr.

Enfin, j'ai deux classe, une NSView(maVue)  et une NSObject test)
Dans maVue je dessine et dans test j'ai une IBAction.
Quand j'utilise mon IBAction je veux modifier une variable de maVue.
Mais rien ne se passe ... Je pense que c'est mon setNeedsDisplay qui ne marche pas mais je comprends pas pourquoi là  ...

Voici mon code de ma classe test :

.h

#import &lt;Cocoa/Cocoa.h&gt;<br />#import &quot;libgraph.h&quot;<br />#import &quot;maVue.h&quot;<br /><br />@class maVue;<br /><br />@interface test : NSObject {<br />	IBOutlet maVue *myView;<br /><br />}<br />@property(nonatomic,retain) IBOutlet maVue *myView;<br /><br />-(IBAction)test:(id)sender;<br /><br />@end<br />


.m

#import &quot;test.h&quot;<br /><br />@implementation test<br />@synthesize myView;<br /><br />-(IBAction)test:(id)sender<br />{<br />	/* J&#39;alloue une variable de type maVue */<br />	myView = [[maVue alloc]init];<br />	<br />	<br />	/* Je modifie une variable de ma classe maVue */<br />	myView.afficher = 1; <br />	<br />	/* Je rafraichi mon dessin */<br />	[myView setNeedsDisplay:YES];<br />	<br />		<br />}


Je vois pas où est mon erreur ...
ps : j'ai bien relié maVue à  ma classe test sous IB .


Merci ^^

Réponses

  • mpergandmpergand Membre
    06:39 modifié #2
    dans 1236881693:

    /* J'alloue une variable de type maVue */
    myView = [[maVue alloc]init];


    Supprime cette ligne et ça ira mieux  ;)

  • NoNo Membre
    06:39 modifié #3
    Le [[maVue alloc] init] ne correspond à  rien.
    Tu créés une vue qui n'est reliée à  aucune vue parente, et donc qui n'est pas à  l'écran (donc pas visible).
    En plus tu écrases la vue créée dans IB et dont le pointeur est dans l'outlet.
    Bref y'a du boulot sur la compréhension du système d'affichage sous cocoa.
  • CeetixCeetix Membre
    06:39 modifié #4
    Rolalala, oui en effet ... 
    MErci en tout cas NO .
  • AliGatorAliGator Membre, Modérateur
    mars 2009 modifié #5
    Oui on seulement myView est déjà  un IBOutlet donc est sensé être connecté à  une vue créée dans IB du coup tu n'as pas à  l'instancier par code comme tu fais...
    Mais en plus si ce n'était pas une vue déjà  créée et reliée à  ton IBOutlet mais que tu voulais créer la vue par code, il faudrait :
    (1) utiliser initWithFrame et non init tout court (comme tu le mets d'ailleurs dans ton titre de sujet, pourquoi tu n'as pas fait l'erreur dans ton code ? arf)
    (2) ajouter la vue créer en tant que subView d'une vue parente si tu veux la voir un jour ;)
  • CeetixCeetix Membre
    06:39 modifié #6
    Ok merci pour Ali pour ces explications, je comprends mieux .
  • schlumschlum Membre
    06:39 modifié #7
    T'as vraiment des soucis pour comprendre la différence entre alloué dans le .nib et dans le programme toi hein  :P
  • CeetixCeetix Membre
    06:39 modifié #8
    Oui bon bah roo ca va  ::) .
    En fait je pense que j'ai du mal à  voir que quand je mets des Outlets mon programme le fait tout seul. Car on ne voit rien en code, juste des tirettes bleues à  relié.... mystique pour moi ^^.
  • schlumschlum Membre
    06:39 modifié #9
    Non, ça n'a rien de mystique... Un IBOutlet n'est qu'un pointeur vers un objet qui existe (alloué au moment du chargement du .nib)

    Regarde les classes NSKeyedArchiver / NSKeyedUnarchiver ; un .nib fonctionne un peu comme ça... ce sont des objets sérialisés qui sont désérialisés au moment du chargement du .nib
  • CeetixCeetix Membre
    06:39 modifié #10
    Façon de parler ^^.
    Ok je vais regarder ça, c'est interessant, je savais pas du tout comment ça se passait.
    Merci
  • GreensourceGreensource Membre
    06:39 modifié #11
    Pff je suis complètement dans ton cas moi aussi! J'ai un mal de chien à  comprendre.  :(
    Ne serais-ce que le départ de l'appli. Après l'entré dans UIApplication, pfffuit je sais rien de ce qui se passe jusqu'au retour dans awakeFromNib de la vue que j'ai déclaré dans IB...Pour l'instant ça reste magique pour moi et c'est vraiment pas agréable.
  • CeetixCeetix Membre
    06:39 modifié #12
    Cool un copain awakeFromNib ^^
  • AliGatorAliGator Membre, Modérateur
    mai 2009 modifié #13
    Un fichier NIB (ou XIB quoi), il faut voir ça comme une sorte d'archive d'objets, interconnectés entre eux.

    1) On définit des objets dans Interface Builder, éventuellement interconnectés entre eux via les IBOutlets & co, on règle leurs propriétés via la palette d'inspecteur, on les positionne les uns par rapport aux autres (positionnement d'une UIView dans une UIView parente en tant que subview, ce qui fait à  la fois la définition de sa frame et l'ajout en tant que subview)... tout ça est fait dans IB donc.

    2) Ensuite, quand on appelle "[tt]initWithNibName: bundle[/tt]", cela va "désarchiver" le fichier NIB, c'est à  dire qu'en interne il va décrypter (désérialiser) le fichier NIB et créer les objets qui sont sérialisés dans le NIB, les "configurer" (ajuster leurs propriétés) d'après les valeurs que tu as mis dans IB, et faire les connexions (IBOutlets) que tu as faites dans IB, pour faire pointer ces IBOutlets (qui ne sont en fait que des variables d'instance) vers les objets créés par le désarchivage. C'est pour ça que sur chaque objet créé par le "désarchivage" d'un NIB est appelée la méhode "initWithCoder" et non juste "init" ou "initWithFrame" : c'est bien une désérialisation qui est effectuée, comme quand on transforme un objet pour l'encoder dans un fichier, puis qu'on le recharge ensuite depuis le fichier plus tard.

    3) Donc [tt]initWithNibName:bundle:[/tt] désarchive le NIB et va permettre de remplacer tout plein de alloc/init qu'on aurait fait par code sinon pour créer tous les objets qui sont sérialisés dans le NIB, tout plein de objet.machin = truc pour configurer les propriétés des objets ainsi créés, ou l'attribution des IBActions, ou les addSubview pour créer la hiérarchie des vues... Bref ça gagne quand même un temps fou et un sacré paquet de lignes de code.

    4) Il n'y a que les objets "Proxy" de votre NIB qui ne sont pas créés lors du désarchivage dudit NIB, et ne servent que de "placeholder", pour représenter un objet existant. C'est rarement utilisé... sauf dans le cas particulier et important du "File's Owner", qui représente l'objet sur lequel on appelle initWithNibName justement, et qui a donc demandé le désarchivage du NIB.
    Une fois que tous les objets ont été désarchivés du NIB, donc alloués et créés à  cette occasion, la méthode "awakeFromNib" est appellée sur chacun. Donc contrairement au cas de initWithCoder appellé au moment où l'objet est créé par le désarchivage du NIB, dans le cas de awakeFromNib on est assuré que tous les autres objets du NIB sont créés donc on peut appeler des méthodes dessus, ou être sûr que les IBOutlets sont bien connectés.







    Ensuite pour répondre à  Greensource, la méthode UIApplicationMain (appellée dans le main de main.m) est en effet un peu magique... en fait elle fait un sacré paquet de trucs :

    1) Elle va initialiser tout ce qu'il faut pour l'application, puis regarder dans le fichier Info.plist s'il y a un "Main Nib File" d'indiqué dedans, ce qui est souvent "MainWindow" par défaut. Si c'est le cas, l'application va automatiquement appeller toute seule initWithNibName:bundle: en interne pour t'éviter d'avoir à  le faire, et charger ainsi automatiquement le NIB principal au lancement de ton appli. Si tu ne précises pas de "Main Nib File" dans le Info.plist, c'est à  toi soit de charger l'interface, soit tout par code, soit avec un NIB en appelant initWithNibName sur un objet à  toi...

    2) Elle crée ensuite une boucle (runLoop) qui va boucler jusqu'à  ce qu'on demande à  l'application de quitter.

    3) Dans cette boucle, elle gère les événements : elle dépile les événements genre touchers écran, notifications, etc... de la pile et les traite puis elle appelle les bonnes méthodes en conséquence.
  • CeetixCeetix Membre
    06:39 modifié #14
    Merci pour ces explications complètes Ali, ça clarifie les choses  ;)
  • GreensourceGreensource Membre
    06:39 modifié #15
    En effet, ça fait pas de mal d'avoir des explications, merci!
    Je me pose une question à  propos de ce que tu dis. On dirais qu'on peut soit créer une vue avec IB soit par code. Mais moi j'aimerais pouvoir faire juste le minimum avec IB:
    Créer une view mère qui contient ma vue Plateau et en dessous une vue barre d'état. Le tout assez vierge et ensuite via mon code, dessiner les cases sur la vue board et des boutons sur la vue barre d'état. J'ai vaguement cru comprendre qu'il fallait utiliser loadView du controllerView.
  • schlumschlum Membre
    06:39 modifié #16
    dans 1236973001:

    Un fichier NIB (ou XIB quoi), il faut voir ça comme une sorte d'archive d'objets, interconnectés entre eux.
    On définit des objets dans Interface Builder, éventuellement interconnectés entre eux via les IBOutlets & co, on règle leurs propriétés via la palette d'inspecteur, on les positionne les uns par rapport aux autres (positionnement d'une UIView dans une UIView parente en tant que subview, ce qui fait à  la fois la définition de sa frame et l'ajout en tant que subview)... tout ça est fait dans IB donc [...]


    Tous les suffrages disent que tu es plus clair que moi  :)
    Pourtant c'est pas faute d'avoir essayé et dans plusieurs sujets  :P


    Enfin... je pense qu'il y a des lectures de " Cocoa par la Pratique " qui se perdent  >:)
  • GreensourceGreensource Membre
    mars 2009 modifié #17
    Pourtant c'est pas fautes de l'avoir lu le bouquin je t'assure! Même si j'ai pas tout fini. Mais le souci c'est que tout passe par IB et que lorsque tu as besoin de le faire en programmation, et bien c'est un tout autre problème je trouve.
    J'ai pas trouver de bon exemple en tout cas pour l'instant.
  • CeetixCeetix Membre
    06:39 modifié #18
    Eu pareil pour moi, c'est pas faute d'avoir commencé ^^
    Oui je suis d'accord avec Greensource.
  • schlumschlum Membre
    06:39 modifié #19
    Pourtant il parle partout d'" instancier " des objets dans IB  :P
  • AliGatorAliGator Membre, Modérateur
    06:39 modifié #20
    Eh oui Schlum, il paraà®t que je suis pédagogue, c'est pas ma faute c'est pas moi qui le dit ^^

    En effet pour créer la vue entièrement par code et non par IB, il faut mettre le code dans loadView.
    Par contre si vous voulez compléter une interface chargée par IB, et pas complètement tout créer "from scratch", il faut "attendre" que les éléments créés par le NIB soient prêts ! Par exemple si dans votre XIB vous avez placé une vue et un bouton dedans, juste ça, et que vous voulez compléter par code en rajoutant 3 sous-vues à  votre vue... Ben il va falloir faire des "addSubview" sur la vue en question... donc encore faut-il que cette dernière ait été créée précédemment. Car si vous essayez de le faire avant que la vue ne soit créée par IB, vous n'allez pas aller loin :)

    loadView est appellé sur le ViewController lorsque ce dernier a besoin de charger sa vue, c'est donc ici qu'il faut mettre le code si on crée l'interface entièrement par code. Par contre c'est viewDidLoad qui est appelé juste après que le ViewController a chargé sa vue, celle qu'il a créé en désarchivant le NIB par exemple. Donc c'est là  qu'il faut mettre le code pour rajouter des éléments d'interface au bout de vue créé par le NIB.

    Si vous vouliez rajouter 3 vues de taille 100x100 chacune dans la vue principale du ViewController, l'une en 0,0 l'autre en 0,120 et la 3e en 0,240 par exemple, la 2e étant une ImageView avec l'image "Toto.png" (présente dans les resources de votre projet) :
    - dans IB c'est pas méchant, il suffit de faire glisser/déposer 2 UIViews et une UIImageView de la palette "Library" vers la vue de votr ViewController, les positionner, et choisir "Toto" dans le menu déroulant des images pour l'ImageView.
    - Par code ça donnerait :
    -(void)viewDidLoad<br />{<br />&nbsp; UIView* v1 = [[UIView alloc] initWithFrame: CGRectMake(0,0,100,100)];<br />&nbsp; [self.view addSubview:v1];<br />&nbsp; [v1 release];<br />&nbsp; UIImageView* v2 = [[UIImageView alloc] initWithFrame: CGRectMake(0,120,100,100)];<br />&nbsp; v2.image = [UIImage imageNamed:&quot;Toto.png&quot;];<br />&nbsp; [self.view addSubview:v2];<br />&nbsp; [v1 release];<br />&nbsp; UIView* v3 = [[UIView alloc] initWithFrame: CGRectMake(0,240,100,100)];<br />&nbsp; [self.view addSubview:v3];<br />&nbsp; [v1 release];<br /><br />}
    
    Et encore là  je n'ai pas réglé bcp de propriétés. Si dans IB vous avez des cases cochées/décochées dans la palette d'inspecteur (genre userInteractionEnabled) ou des menus déroulants que vous modifiez (genre le mode de votre UIView de ScaleToFit à  ScaleToFill ou autre) il faut bien sûr effectuer ces réglages par code à  la place dans la solution en code.
  • schlumschlum Membre
    06:39 modifié #21
    dans 1237036487:

    Eh oui Schlum, il paraà®t que je suis pédagogue, c'est pas ma faute c'est pas moi qui le dit ^^


    C'est une pédagogie différente  :P
    Moi j'aime pas leur mâcher le boulot, donc je donne des pistes d'investigation pour les forcer à  aller plus loin !

    Ah là là , qu'est-ce qu'ils sont flemmards les jeunes de nos jours  :)
  • CeetixCeetix Membre
    06:39 modifié #22
    Hop hop hop, déjà  les jeunes ont tjs été flémard et de deux perso, je veux les pistes .
    Par contre si vraiment je galère et comprends pas en savoir un peu plus n'est pas de refus :D
  • AliGatorAliGator Membre, Modérateur
    06:39 modifié #23
    Bah là  j'ai pas mâché le travail avec du code, j'ai expliqué un concept :P
    Moi aussi je suis pas fan de mâcher le travail, sinon ça leur apprend pas à  chercher par eux-mêmes...
    Seulement parfois un exemple vaut mieux qu'un long discours ;)
    Bon ok là  j'ai donné du code, mais c'était du code d'exemple justement, et pas le code tout fait dont ils avaient forcément besoin, y'a encore un sacré paquet à  adapter ;)
    Par exemple je n'ai pas parlé des contraintes de redimentionnement des vues quand on les places dans une subView, ces contraintes que l'on fixe dans l'onglet avec l'icône de la petite réglette dans la palette d'Inspecteur dans IB, pour dire quelles dimensions doivent être fixes et lesquelles sont flexibles quand la vue se redimentionne... Ca aussi faut le spécifier par code quand on crée l'interface par code... Et c'est pour toutes ces choses là  que IB reste quand même un outil bien pratique (mais on faut savoir faire les 2 car parfois passer par le code même pour la création d'interface ça peut être utile)
Connectez-vous ou Inscrivez-vous pour répondre.