Core Animation

Philippe49Philippe49 Membre
décembre 2007 modifié dans API AppKit #1
Bon, j'essaie de débroussailler Core Animation et les autres.
On peut commencer par faire l'exemple Menu du Core Animation Programming Guide , mais c'est un peu lourd à  digérer en cette période de fête.

Voici une mise en bouche plus à  ma portée : CALayer (1)
Merci d'avance de vos remarques et suggestions.
«1

Réponses

  • psychoh13psychoh13 Mothership Developer Membre
    06:52 modifié #2
    Première suggestion : au lieu d'avoir un IBOutlet pour ta fenêtre, utilise directement la contentView comme IBOutlet.
  • Philippe49Philippe49 Membre
    06:52 modifié #3
    Je veux bien, mais à  part gagner une ligne de code tu vois un intérêt ?
  • psychoh13psychoh13 Mothership Developer Membre
    06:52 modifié #4
    ça sert à  rien, c'est juste au nom de la cohérence. :D

    Sinon, comment ta méthode -setAngle: est-elle appelée ?
  • Philippe49Philippe49 Membre
    décembre 2007 modifié #5
    Ah oui il faut que je le rajoute : binding avec le slider

    voilà  c'est fait.
  • Philippe49Philippe49 Membre
    décembre 2007 modifié #6
    Cela se poursuit avec les properties anchor et position, ainsi que la possibilité de disposer un layer sur une vue dans IB.

    CALayer (2)

  • Philippe49Philippe49 Membre
    décembre 2007 modifié #7
    Troisième page :

    Un Hello World avec un CATextLayer.

    Il faut encore que je complète cette page en montrant que l'utilisation des contraintes graphiques CAConstraint ne sont pas faciles à  adapter à  la rotation du layer (ou pas au point?)  >:D

    [EDIT] Les CAConstraint seront dans la page suivante
  • TchouboudouTchouboudou Membre
    06:52 modifié #8
    Dis donc, on l'arrête plus ! C'est bien en tout cas :) J'attend la suite.
  • Philippe49Philippe49 Membre
    décembre 2007 modifié #9
    dans 1199032401:

    Dis donc, on l'arrête plus ! C'est bien en tout cas :) J'attend la suite.


    Ben quand on est lancé ... et qu'on attend le réveillon  :p   <3 <br />
    J'ai fait une petite modif depuis : le binding comme dans CALAyer01 n'était pas logique 
  • Philippe49Philippe49 Membre
    décembre 2007 modifié #10
    Quatrième page : CAConstraint

    Avec un problème ( == bug ? ) restant à  régler.
  • Philippe49Philippe49 Membre
    janvier 2008 modifié #11
    Cinquième page : Movie_QC_openGL_Capture

    Au menu : disposition dans une matrice de
    • QCCompositionLayer
    • QTMovieLayer
    • CAOpenGLLayer
    • QTCaptureLayer



  • Philippe49Philippe49 Membre
    06:52 modifié #12
    Sixième page : les properties contents et contentsRect

    Mettre une image comme contenu d'un CALayer.


  • psychoh13psychoh13 Mothership Developer Membre
    06:52 modifié #13
    Fais attention tu as une petite erreur dans ton prefix header :

    #define LOGRECT(rect) (fprintf(stderr,#rect),&#092;<br />&nbsp;  fprintf(stderr,&quot; : origin=(%.2f , %.2f) , size=(%.2f , %.2f)&#092;n&quot;&#092; <br />&nbsp;  rect.origin.x,rect.origin.y,rect.size.width,rect.size.height))
    


    À la fin de la deuxième ligne, avant le slash, tu as oublié la virgule.
  • Philippe49Philippe49 Membre
    06:52 modifié #14
    Merci
  • psychoh13psychoh13 Mothership Developer Membre
    06:52 modifié #15
    Bon, j'ai fouillé un peu ton code pour Contents, et je ne comprends pas pourquoi tu utilises le Garbage Collector...
    J'ai simplifié un peu ton code et j'obtiens des résultats très probant. J'avais d'abord essayer ton code mais si par malheur je cliquais deux fois sur "image in" ça plantais l'application...
    Je pense aussi que ton -dealloc est faux, tu envoies le message -release a un objet auquel tu n'as même pas envoyé de -retain ce qui veut dire que tu n'en portes pas la responsabilité.
    Voilà  ce que ça peut donner comme code :

    #import &quot;ViewController.h&quot;<br /><br />@implementation ViewController<br /><br />- (void)awakeFromNib<br />{<br />	CGFloat cornerRadius = 5.0, borderWidth = 3.0;<br />	rootLayer = [[self view] layer];<br />	rootLayer.borderColor = [CGColorCreateGenericRGB(0.6, 0.6, 0.6, 1.0) autorelease];<br />	rootLayer.borderWidth = borderWidth;<br />	rootLayer.cornerRadius = cornerRadius;<br />	rootLayer.contentsGravity = kCAGravityResizeAspect;<br />}<br /><br />- (IBAction)orderImageIn:(id)sender<br />{<br />	// J&#39;ai ajouté ton image au projet, mais ça donnerait la même chose<br />	// avec l&#39;image autre part<br />	NSImage *image = [NSImage imageNamed:@&quot;aquadavinci_ii.jpg&quot;];<br />	<br />	// Il y a un warning ici, soit disant -NSImageRep ne va peut-être pas répondre à <br />	// CGImage, mais ça marche, je pense qu&#39;il manque juste un en-tête à  importer<br />	rootLayer.contents = [[image bestRepresentationForDevice:nil] CGImage];<br />}<br /><br /><br />- (IBAction)orderImageOut:(id)sender<br />{<br />	rootLayer.contents = nil;<br />}<br /><br />- (void)dealloc<br />{<br />	//[rootLayer release]; // je suis vraiment pas sûr qu&#39;il soit utile...<br />	[super dealloc];<br />}<br /><br />@end
    
  • Philippe49Philippe49 Membre
    06:52 modifié #16
    dans 1199378098:

    Bon, j'ai fouillé un peu ton code pour Contents, et je ne comprends pas pourquoi tu utilises le Garbage Collector...
    J'ai simplifié un peu ton code et j'obtiens des résultats très probant. J'avais d'abord essayer ton code mais si par malheur je cliquais deux fois sur "image in" ça plantais l'application...

    oui c'est le sujet de mon post d'aujourd'hui,
    Ce phénomène disparait avec l'option du garbage collecteur. Je ne sais toujours pas pourquoi. La gestion mémoire était clairement sous-jacente, mais le lien n'est pas clair.

    dans 1199378098:

    Je pense aussi que ton -dealloc est faux, tu envoies le message -release a un objet auquel tu n'as même pas envoyé de -retain ce qui veut dire que tu n'en portes pas la responsabilité.

    Tu as raison, j'ai oublié de le retirer (j'avais créé auparavant rootLayer dans le programme)


    Bon j'essaie ton code. J'avais fait un essai semblable avec une NSBitmapImageRep pour la NSImage, mais cela échouait. 
    sans doute parce que je ne mettais pas la transformation CGImage.
  • Philippe49Philippe49 Membre
    janvier 2008 modifié #17
    Ben oui, mais comment fait la NSImageRep fait pour répondre à  la méthode CGImage ?

    Dans la doc, CGImage est une méthode de NSBitmapImageRep.
  • Philippe49Philippe49 Membre
    06:52 modifié #18

    bestRepresentationForDevice renvoie non pas une NSImageRep mais une instance de la sous-classe NSBitmapRep lorsque le fichier est de l'un de ces types TIFF, BMP, JPEG, GIF, PNG, DIB, ICO, (among others).
    (Cocoa Drawing Guide / image basics)


  • psychoh13psychoh13 Mothership Developer Membre
    06:52 modifié #19
    Déjà  une chose est sûre, c'est que NSImageRep est une classe semi-abstraite (semi parce qu'elle a quelques variables d'instance) c'est-à -dire qu'on ne l'instancie pas, les objets qui sont donc retournés sont forcément des instances de l'une de ses sous-classes, par bonheur, mais je ne pense pas que ça soit un hasard, il s'agit ici d'une NSBitmapImageRep.

    Je pense pas que ça soit un hasard, puisque je demande en fait à  la méthode -bestRepresentationForDevice: en passant comme paramètre "nil", que la méthode me retourne la meilleure représentation possible pour l'afficher à  l'écran, ça ne peut donc pas être l'un des autre type possible (par exemple NSEPSImageRep, NSPDFImageRep, etc.)

    Donc c'est pour ça qu'ici la méthode fonctionne.
  • Philippe49Philippe49 Membre
    06:52 modifié #20
    Bon voilà  un code sans warning

    - (IBAction)orderImageIn:(id)sender
    {
    NSImage *image = [NSImage imageNamed:@aquadavinci_ii.jpg];
    NSBitmapImageRep * bitmapImageRep=(id)[image bestRepresentationForDevice:nil] ;
    CGImageRef imageRef=[bitmapImageRep CGImage] ;
    rootLayer.contents =(id)imageRef;
    }


    Mais cela n'explique pas pourquoi le code précédent ne marchait pas sans garbage collector.

  • Philippe49Philippe49 Membre
    06:52 modifié #21
    Ah peut-être une piste d'explication

    si on ne mets pas l'image en ressource ça rebug
    NSString * imagePath=[NSHomeDirectory() stringByAppendingString:@/Pictures/aquadavinci_ii.jpg];
    NSImage *image = [[[NSImage alloc] initWithContentsOfFile:imagePath] autorelease];


    et si on retire l'autorelease ça ne bug plus.

    D'autre part, la doc Cocoa Drawing Guide indique la mise en cache de la représentation.

    Cela signifierait peut-être qu'il ne faut pas faire de release sur l'image (ou le data dans ma version précédente) , et le garbage collector lui gérerais tout cela ?
  • psychoh13psychoh13 Mothership Developer Membre
    06:52 modifié #22
    Moi et le GC ça fait 30, alors je ne peux pas te donner une réponse concluante, cependant je sais que lorsqu'on utilise le garbage collector, déjà  il vaut mieux utiliser soit 100% GC soit pas du tout, l'option "supported" est déconseillée sauf dans le cas d'un framework par exemple.

    Bon, toujours est-il que lorsqu'on utilise le GC normalement les messages -release, -retain et -autorelease ne font strictement rien, ils sont court-circuité, donc peut-être que tu t'es trompé dans ta gestion de mémoire...
  • Philippe49Philippe49 Membre
    janvier 2008 modifié #23
    Merci en tout cas pour ton aide, j'y ai passé la journée sur ce bug, mais cela confirme que dans des situations apparemment absurdes, c'est souvent une erreur de gestion mémoire qui est en cause.


    [EDIT] A ce stade, je pense qu'une CGImage est un "contrôleur" pour l'image, la CGImageSource étant le modèle.
  • Philippe49Philippe49 Membre
    janvier 2008 modifié #24
    dans 1199374272:

    Sixième page : les properties contents et contentsRect

    Mettre une image comme contenu d'un CALayer.


    Mise à  jour de cette page.
  • Philippe49Philippe49 Membre
    janvier 2008 modifié #25
    Septième page : Drag and Drop, paste


  • psychoh13psychoh13 Mothership Developer Membre
    06:52 modifié #26
    Je crois que tu t'es trompé de lien pour commencer. :D
    Tu envoies sur la page Contents.
  • Philippe49Philippe49 Membre
    06:52 modifié #27
    dans 1199616975:

    Tu envoies sur la page Contents.


    Merci, rectification faite.

    Pour la suite, j'aimerais que le NSViewController prenne en charge les évènements.
    Etre le delegate de l'application ou de la mainWindow n'est pas suffisant .
    Bon y a la solution de renvoyer un à  un les messages dans une sous-classe de NSView , mais  :-\\

    Deux pistes :

    1. Changer de classe pour ViewController : En faire un NSResponder : pour l'instant ça n'a pas l'air de marcher
    2. Créer une sous-classe ViewWithDelegate , avec un IBOutlet delegate, et la redéfinition des méthodes forwardInvocation: et methodSignatureForSelector:  humm :o j'ai jamais fait ça  :o


    Qu'en penses-tu ?



    -(void) forwardInvocation:(NSInvocation *) invocation <br />{<br />&nbsp; &nbsp; if([delegate respondsToSelector:[invocation selector]]){<br />&nbsp; &nbsp; &nbsp;  [invocation invokeWithTarget:delegate];<br />&nbsp; &nbsp; } else {<br />&nbsp; &nbsp; &nbsp;  [super forwardInvocation:invocation];<br />&nbsp; &nbsp; }<br />} <br /><br />-(NSMethodSignature*) methodSignatureForSelector:(SEL) selector<br />{<br />&nbsp; &nbsp; NSMethodSignature* signature=[[self class]&nbsp; instanceMethodSignatureForSelector:selector];<br />&nbsp; &nbsp; NSMethodSignature* delegateSignature=[[delegate class]&nbsp; instanceMethodSignatureForSelector:selector];<br />&nbsp; &nbsp; if(delegateSignature){<br />&nbsp; &nbsp; &nbsp; &nbsp;  return delegateSignature;<br />&nbsp; &nbsp; } else if (signature) {<br />&nbsp; &nbsp; &nbsp; &nbsp;  return signature;<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; return nil;&nbsp; // là  j&#39;ai un doute<br />}
    
  • psychoh13psychoh13 Mothership Developer Membre
    06:52 modifié #28
    Déjà  une chose est sûr la redirection de messages ne t'aidera pas à  faire gérer les événements par ton délégué... NSResponder implémente les méthodes qui gèrent les événements, donc si ta sous-classe de NSResponder n'implémente pas la méthode, ce sera l'implémentation de NSResponder qui sera utilisé... Le redirection est utilisée bien plus tard.

    Sinon, juste pour te rassurer, -methodSignatureForSelector: retourne bien nil si elle ne trouve pas la méthode.

    À part ça, la classe NSViewController dont hérite ViewController est déjà  une sous-classe de NSResponder, donc pas besoin d'en changer.  ;D (il manque vraiment de smileys ce forum :().

    Pour l'instant, la seule solution valable que je trouve c'est de faire une sous-classe de NSView pour gérer les événements, c'est d'ailleurs la méthode la plus logique...

    Mais je vais m'y pencher un peu plus...
  • psychoh13psychoh13 Mothership Developer Membre
    06:52 modifié #29
    Bon, j'ai trouvé ta solution !

    Comme je l'ai dit, NSViewController est déjà  un NSResponder, donc ta classe l'est aussi... Donc pour que ton objet accepte les événements il faut réunir quelques conditions.

    Pour commencer, en lisant la doc de NSResponder on voit que tous les événements ont pour implémentation par défaut de passer l'événement au "next responder".
    Il faut aussi savoir que NSView ne surcharge aucune méthode d'événement de NSResponder, donc par conséquent il suffit de se mettre dans la chaà®ne des responders pour récupérer les événements de la vue, donc il suffit de faire, dans -awakeFromNib, un simple :
    [[self view] setNextResponder:self];
    


    Comme ça tu récupère tous les événements que ne gère pas ta vue, c'est-à -dire tous les événement.
    Avec ça il te suffit de surcharger les méthodes que tu veux... Par contre, ce n'est valable que pour les événements de souris (mouseDown:, mouseUp:, scrollWheel:, etc.) car là  on sait où ça se trouve...
    Mais pour les événements clavier c'est une autre affaire.

    Pour répondre aux événement clavier, il faut connaà®tre l'objet qui les reçoit en premier... Il en faut bien un puisqu'à  la fin ça émet un bip, alors on a le choix... Soit on sous-classe NSView et on lui dit de répondre aux événements clavier en surchargeant la méthode "-acceptsFirstResponder" soit bah on cherche un objet qui y répond...

    Alors j'ai commencé par tester en utilisant le next responder de NSApp, mais il se trouve que NSApplication surcharge toutes les méthodes de NSResponder pour les mettre à  sa sauce et terminer la chaà®ne, donc ça sert à  rien.
    En revanche, j'ai testé sur la fenêtre de notre vue, et il se trouve que ça marche...
    Donc, il faut se mettre en next responder de la fenêtre pour récupérer tous ses événements...
    Par contre on récupérera aussi les événements de souris qui sont hors de notre vue... Si celle-ci ne prend pas toute la fenêtre ça peut être chiant... Le seul moyen que je vois pour parer à  ce problème serait de vérifier que l'événement se trouve ou non dans la frame de la vue.


    D'ailleurs à  ce propos, tu ajoutes toujours une nouvelle vue dans ta fenêtre, mais si tu ne mets jamais plus d'un objet dans ta fenêtre tu peux toujours utiliser la content view.
  • psychoh13psychoh13 Mothership Developer Membre
    06:52 modifié #30
    [size=20pt]ATTENTION :[/size]
    Dans la documentation d'Apple sur les événements, il est dit qu'il ne faut pas envoyer -setNextResponder: sur une NSView parce que lors de l'ajoute d'une sous-vue son next responder est directement relié à  sa super-vue, et la méthode setNextResponder: de NSView ne modifie pas le next responder de son nouveau next responder, ce qui fait que si on utilise cette méthode, la chaà®ne sera coupée. Alors qu'avec NSWindow ça marche directement.

    Donc, il faut faire un choix, soit on se fait chier et on se met à  deux endroits de la chaà®ne en faisant attention de ne pas la couper en plein milieu (c'est-à -dire qu'il faut récupérer le next responder de la vue et se le mettre à  soi avant de se mettre soi-même en next responder de la vue).
    Soit, on gère uniquement les événements à  partir de la NSWindow...

    La deuxième solution est plus simple, est pour être sûr d'être sur la vue pour les événements de souris il suffit de tester la position de la souris...
  • Philippe49Philippe49 Membre
    janvier 2008 modifié #31
    dans 1199622063:

    À part ça, la classe NSViewController dont hérite ViewController est déjà  une sous-classe de NSResponder,

    Je ne l'avais pas vu cela. Cela simplifie tout.


    dans 1199624983:


    Donc, il faut faire un choix, soit on se fait chier et on se met à  deux endroits de la chaà®ne en faisant attention de ne pas la couper en plein milieu (c'est-à -dire qu'il faut récupérer le next responder de la vue et se le mettre à  soi avant de se mettre soi-même en next responder de la vue).


    NSResponder * viewNextResponder=[[self view] nextResponder];
    [[self view] setNextResponder:self]
    [self setNextResponder: viewNextResponder];


    Ne marche pas pour les événements clavier.

    dans 1199624983:

    La deuxième solution est plus simple

    On peut couper la chaà®ne au niveau de la fenêtre de l'appli ?
    Au-dessus ce doit être NSApp ? 


    NSResponder * windowNextResponder=[[[self view] window] nextResponder];
    [[self view] setNextResponder:self]
    [self setNextResponder: windowNextResponder];  ou [self setNextResponder: nil];  ou rien ?

Connectez-vous ou Inscrivez-vous pour répondre.