Slide entre deux views, à  la manière de UINavigationController sur l'iPhone.

Nebuchad34Nebuchad34 Membre
septembre 2009 modifié dans API AppKit #1
J'ai besoin de réaliser une interface "à  la front row" et j'aimerai utiliser le même principe que sur l'iPhone.

Je vois bien comment implémenter la chose, j'ai juste un problème à  résoudre :
Comment faire un "slide" entre deux NSView (comme le fait le UINavigationController lorsqu'on "push" un autre ViewController)

J'imagine qu' Eaglelouk pourrait me répondre, vu qu'il a mis en place ce genre de choses dans Ecoute.

Réponses

  • CéroceCéroce Membre, Modérateur
    23:33 modifié #2
    Jette un oe“il à  la classe NSViewAnimation.
  • Nebuchad34Nebuchad34 Membre
    23:33 modifié #3
    NSViewAnimation se limite au fade-in fade-out et changement de frame. Il ne permet pas de faire du "slide-in slide-out"
  • Philippe49Philippe49 Membre
    23:33 modifié #4
    CoreAnimation et CATransition fait cela en souplesse
  • Philippe49Philippe49 Membre
    septembre 2009 modifié #5
    Voilà  un exemple pour faire une transition "Curl" entre un NSView et une Image View, placé dans le code d'un NSViewController dont la vue contient les deux vues que l'on échange :

    <br />-(IBAction) curl:(id) sender {<br />	// create an image with myView<br />	CIImage * firstImage=[CIImage CIImageInView:myView];	<br /><br />	// create an image with imageView<br />	CIImage * secondImage=[CIImage CIImageInView:imageView];<br />	<br />	// backSide<br />	CIImage * backsideImage=[CIImage imageWithColor:[CIColor colorWithString:@&quot;0. 0. 0.2 1.&quot;]];	<br />	<br />	// create a shadow image	<br />	NSString * shadingImagePath=[[NSBundle mainBundle] pathForResource:@&quot;restrictedshine&quot; ofType:@&quot;png&quot;];<br />	CIImage * shadingImage=[[CIImage alloc] initWithContentsOfURL:[NSURL fileURLWithPath:shadingImagePath]];<br /><br />	// the filter<br />	CIFilter * pageCurlFilter=[CIFilter filterWithName:@&quot;CIPageCurlTransition&quot;];<br />	[pageCurlFilter setDefaults];&nbsp; <br />	// setting images<br />	if([myView superview]==self.view) {<br />		[pageCurlFilter setValue:firstImage forKey:@&quot;inputImage&quot;];<br />		[pageCurlFilter setValue:secondImage forKey:@&quot;inputTargetImage&quot;];<br />		[pageCurlFilter setValue:backsideImage forKey:@&quot;inputBacksideImage&quot;];	<br />		[pageCurlFilter setValue:shadingImage forKey:@&quot;inputShadingImage&quot;];	<br />		[myView removeFromSuperviewWithoutNeedingDisplay];<br />		[self.view addSubview:imageView];<br />	} else {<br />		[pageCurlFilter setValue:secondImage forKey:@&quot;inputImage&quot;];<br />		[pageCurlFilter setValue:firstImage forKey:@&quot;inputTargetImage&quot;];<br />		[pageCurlFilter setValue:backsideImage forKey:@&quot;inputBacksideImage&quot;];	<br />		[pageCurlFilter setValue:shadingImage forKey:@&quot;inputShadingImage&quot;];			<br />		[imageView removeFromSuperviewWithoutNeedingDisplay];<br />		[self.view addSubview:myView];<br /><br />	}<br />	// setting geometry for curling <br />	[pageCurlFilter setValue:[NSNumber numberWithFloat:M_PI-0.2] forKey:@&quot;inputAngle&quot;];<br />	[pageCurlFilter setValue:[NSNumber numberWithFloat:50.] forKey:@&quot;inputRadius&quot;];	<br />	[pageCurlFilter setValue:[CIVector vectorWithX:0.0 Y:0.0 Z:self.view.layer.bounds.size.width W:self.view.layer.bounds.size.height] forKey:@&quot;inputExtent&quot;];	<br />	[self.view.layer setFilters:[NSArray arrayWithObject:pageCurlFilter]];<br />	[pageCurlFilter setName:@&quot;pageCurlFilter&quot;];<br />	<br />	[self animeFilter:pageCurlFilter];	<br />}<br />
    


    <br />-(void) animeFilter:(CIFilter*)filter {<br />	CATransition * curlTransition=[CATransition animation];<br />	curlTransition.filter=filter;<br />	curlTransition.startProgress=0.0;<br />	curlTransition.endProgress=1.0;<br />	curlTransition.delegate=self;<br />	curlTransition.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];<br />	curlTransition.duration=3.;<br />	[self.view.layer addAnimation:curlTransition forKey:@&quot;pageCurlFilter&quot;];<br />}<br />
    


    La méthode CIImageInView étant à  définir dans une catégorie de CIImage à  l'aide de initWithBitmapImageRep:
  • Nebuchad34Nebuchad34 Membre
    23:33 modifié #6
    Merci.

    Mais apparemment, il n'est pas possible de faire une transition "slidein-slideout" de cette manière, comme sur l'iPhone, si ?
  • Philippe49Philippe49 Membre
    23:33 modifié #7
    Si, regarde la doc sur les CIFilter tu seras surpris de tout ce que l'on peut faire
  • Philippe49Philippe49 Membre
    septembre 2009 modifié #8
    D'ailleurs certains filtres sont paramétrables dans IB; Quand j'avais essayé il y a un an et demi ou ici, cela ne marchait que dans certaines conditions, mais il faudrait y retourner voir.
  • Nebuchad34Nebuchad34 Membre
    23:33 modifié #9
    TRès bien, merci.

    Je vais regarder ça de plus près à  tête reposée. Je viendrais vous donner mes avancements.
  • Philippe49Philippe49 Membre
    23:33 modifié #10
    Il y a surement une solution aussi avec CATransition avec le layer d'une view qui accueillerais successivement les deux view ... je n'ai plus le code sous la main ...


    Common Transition Types

    These constants specify the transition types that can be used with the type property.

    NSString * const kCATransitionFade;
    NSString * const kCATransitionMoveIn;
    NSString * const kCATransitionPush;
    NSString * const kCATransitionReveal;
    Constants
    kCATransitionFade
    The layer's content fades as it becomes visible or hidden.
    Available in iPhone OS 2.0 and later.
    Declared in CAAnimation.h.
    kCATransitionMoveIn
    The layer's content slides into place over any existing content. The “Common Transition Subtypes” are used with this transition.
    Available in iPhone OS 2.0 and later.
    Declared in CAAnimation.h.
    kCATransitionPush
    The layer's content pushes any existing content as it slides into place. The “Common Transition Subtypes” are used with this transition.
    Available in iPhone OS 2.0 and later.
    Declared in CAAnimation.h.
    kCATransitionReveal
    The layer's content is revealed gradually in the direction specified by the transition subtype. The “Common Transition Subtypes” are used with this transition.
    Available in iPhone OS 2.0 and later.
    Declared in CAAnimation.h.
    Declared In
    CATransition.h
    Common Transition Subtypes

    These constants specify the direction of motion-based transitions. They are used with the subtype property.

    NSString * const kCATransitionFromRight;
    NSString * const kCATransitionFromLeft;
    NSString * const kCATransitionFromTop;
    NSString * const kCATransitionFromBottom;
     
  • Nebuchad34Nebuchad34 Membre
    septembre 2009 modifié #11
    Alor, ça ne marche pas...  :)

    Qu'est-ce qui cloche ? voici mon code, j'ai juste adapté ce que tu m'avais donné.
    - (void)slideFromViewController:(PMViewController *)viewControllerToHide<br />			&nbsp;  toViewController:(PMViewController *)viewControllerToShow<br />{<br />	// create an image with the viewcontroller&#39;s view to hide<br />	CIImage * firstImage=[CIImage CIImageWithView:viewControllerToHide.view];<br />	<br />	// create an image with the viewcontroller&#39;s view to show<br />	CIImage * secondImage=[CIImage CIImageWithView:viewControllerToShow.view];<br /><br />	// the filter<br />	CIFilter * transitionFilter=[CIFilter filterWithName:@&quot;CISwipeTransition&quot;];<br />	[transitionFilter setDefaults];&nbsp; <br />	<br />	// setting images<br />	[transitionFilter setValue:firstImage forKey:@&quot;inputImage&quot;];<br />	[transitionFilter setValue:secondImage forKey:@&quot;inputTargetImage&quot;];<br />	<br />	[viewControllerToHide.view removeFromSuperviewWithoutNeedingDisplay];<br />	[containerView addSubview:viewControllerToShow.view];<br /><br />	[containerView.layer setFilters:[NSArray arrayWithObject:transitionFilter]];<br />	[transitionFilter setName:@&quot;transitionFilter&quot;];<br />	<br />	[self animeFilter:transitionFilter];	<br />}<br /><br />-(void) animeFilter:(CIFilter*)filter {<br />	CATransition * transition=[CATransition animation];<br />	transition.filter=filter;<br />	transition.startProgress=0.0;<br />	transition.endProgress=1.0;<br />	transition.delegate=self;<br />	transition.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];<br />	transition.duration=3.;<br />	[containerView.layer addAnimation:transition forKey:@&quot;transitionFilter&quot;];<br />}
    



    et j'ai fait une catégorie sur CIImage comme suggéré,
    J'espère que la méthode suivante est correct :
    @implementation CIImage (CIImageWithView)<br /><br />+(CIImage *)CIImageWithView:(NSView *)view<br />{	<br />	NSBitmapImageRep *imageRep = [view bitmapImageRepForCachingDisplayInRect:[view bounds]];<br />	CIImage *image = [[CIImage alloc] initWithBitmapImageRep:imageRep];<br />	return [image autorelease];<br />}<br /><br />@end
    


  • Philippe49Philippe49 Membre
    23:33 modifié #12
    Ok pour la méthode CIImageInRect:

    Peut-être le filtre incomplètement renseigné . Je ferais un NSLog sur transitionFilter à  différents endroits pour voir si les paramètres du filtre sont bien renseignés.
  • Philippe49Philippe49 Membre
    23:33 modifié #13
    Un NSLog sur une telle transition donne les champs suivants :

    2009-09-19 20:14:28.766 FlipView[2626:a0f] {CISwipeTransition {<br />&nbsp; &nbsp; inputAngle = &quot;&lt;null&gt;&quot;;<br />&nbsp; &nbsp; inputColor = &quot;&lt;null&gt;&quot;;<br />&nbsp; &nbsp; inputExtent = &quot;&lt;null&gt;&quot;;<br />&nbsp; &nbsp; inputImage = &quot;&lt;null&gt;&quot;;<br />&nbsp; &nbsp; inputOpacity = &quot;&lt;null&gt;&quot;;<br />&nbsp; &nbsp; inputTargetImage = &quot;&lt;null&gt;&quot;;<br />&nbsp; &nbsp; inputTime = &quot;&lt;null&gt;&quot;;<br />&nbsp; &nbsp; inputWidth = &quot;&lt;null&gt;&quot;;<br />}}
    
  • Nebuchad34Nebuchad34 Membre
    septembre 2009 modifié #14
    Pfiou ça me lourde, on est obligé de renseigner tous les champs ?
    Je pensais qu'en dehors de input et target image il y avait des valeurs par défaut.

    En plus j'avais pris ça au hasard je ne sais même pas quel genre de tansition ça fait.

    Le kCATransitionPush semble parfait, mais comment l'utiliser ?
  • Philippe49Philippe49 Membre
    septembre 2009 modifié #15
    Essai concluant avec un code qui ressemble au tien :
    <br />-(IBAction) curl:(id) sender {<br />	// create an image with backgroundView<br />	CIImage * firstImage=[CIImage CIImageInView:backgroundView];	<br /><br />	// create an image with imageView<br />	CIImage * secondImage=[CIImage CIImageInView:imageView];<br />	<br />	// backSide<br />	CIImage * backsideImage=[CIImage imageWithColor:[CIColor colorWithString:@&quot;0. 0. 0.2 1.&quot;]];	<br />	<br /><br />	// the filter<br />	CIFilter * swipeFilter=[CIFilter filterWithName:@&quot;CISwipeTransition&quot;];<br />	[swipeFilter setDefaults];<br />	[swipeFilter setValue:firstImage forKey:@&quot;inputImage&quot;];<br />	[swipeFilter setValue:secondImage forKey:@&quot;inputTargetImage&quot;];<br /><br />	<br /><br />	// setting images<br />	[self.view.layer setFilters:[NSArray arrayWithObject:swipeFilter]];<br /><br />	[swipeFilter setName:@&quot;swipeFilter&quot;];<br />	[self animeFilter:swipeFilter];	<br /><br />}<br />-(void) animeFilter:(CIFilter*)filter {<br />	CATransition * curlTransition=[CATransition animation];<br />	curlTransition.filter=filter;<br />	curlTransition.startProgress=0.0;<br />	curlTransition.endProgress=1.0;<br />	curlTransition.delegate=self;<br />	curlTransition.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];<br />	curlTransition.duration=3.;<br />	[self.view.layer addAnimation:curlTransition forKey:@&quot;swipeFilter&quot;];<br />}
    

  • Nebuchad34Nebuchad34 Membre
    septembre 2009 modifié #16
    Grrrrrrrrr

    Y'a rien qui marche.  :crackboom:-
  • Philippe49Philippe49 Membre
    23:33 modifié #17
    dans 1253385081:

    En plus j'avais pris ça au hasard je ne sais même pas quel genre de tansition ça fait.

    cela fait comme une barre semi-ombrée qui dévoile la vue


    dans 1253385081:

    Le kCATransitionPush semble parfait, mais comment l'utiliser ?

    Quasiment comme dans animeFilter: , en y faisant le changement.
    Pour être plus précis, je te réponds un peu plus tard ..
  • Philippe49Philippe49 Membre
    23:33 modifié #18
    CISwipe
  • Nebuchad34Nebuchad34 Membre
    23:33 modifié #19
    Ah , premier semblant d'animations avec kCATransitionPush... j'y viens
  • Nebuchad34Nebuchad34 Membre
    23:33 modifié #20
    ça a l'air de fonctionner.  j'approfondi un peu et je reviens en parler ici.
  • Nebuchad34Nebuchad34 Membre
    23:33 modifié #21
    Je viens confirmer que kCATransitionPush fait des merveilles !
    C'est exactement ce que je voulais et c'est très simple à  faire.
  • Philippe49Philippe49 Membre
    septembre 2009 modifié #22
    Troisième solution, que je viens de retrouver dans une de mes applis (trop vieux pour que je m'en rappelle plus tôt : Février 2009 !):

    [CATransaction begin];<br />	[CATransaction setValue:[NSNumber numberWithFloat:1] forKey:kCATransactionAnimationDuration];<br />	outView.layer.position=invisiblePosition;<br />	inView.layer.position=visiblePosition;<br />	[CATransaction commit];<br />
    
  • CéroceCéroce Membre, Modérateur
    23:33 modifié #23
    dans 1253305194:

    NSViewAnimation se limite au fade-in fade-out et changement de frame. Il ne permet pas de faire du "slide-in slide-out"

    Pourtant, c'est très exactement ce que je fais dans PortraiMatic.
  • 23:33 modifié #24
    NSViewAnimation suffit largement à  mon goût.. Après tout l'iphone, à  mon humble avis, lors d'une transition ne fait que qu'un slide + fade out (de la vue précédente) + fade in (de la nouvelle vue).
    Et étant donné qu'on peut jouer sur l'alpha avec NSViewAnimation..
  • 23:33 modifié #25
    Je me suis pas fait chier, je dois filer chercher ma douce  :kicking:

    Enjoy.

    (Tu peux largement simplifier le truc, moi j'ai fait du copier-coller et j'ai changé 2-3 trucs pour chacune des actions)
  • Nebuchad34Nebuchad34 Membre
    23:33 modifié #26
    Le résultat est aussi très bien.

    Je vais comparer avec kCATransitionPush, mais il me semble que NSViewAnimation donne quelque chose d'effectivement plsu proche du fonctionnement de l'iphone !
    ;)
  • CéroceCéroce Membre, Modérateur
    23:33 modifié #27
    Voici un extrait de mon code:
    <br />	// Create the animation<br />	NSArray* animations = [NSArray arrayWithObjects:<br />		[NSDictionary dictionaryWithObjectsAndKeys:	oldView, NSViewAnimationTargetKey,<br />		[NSValue valueWithRect:oldDestFrame], NSViewAnimationEndFrameKey, <br />		nil],<br />		[NSDictionary dictionaryWithObjectsAndKeys:	newView, NSViewAnimationTargetKey,<br />		[NSValue valueWithRect:newDestFrame], NSViewAnimationEndFrameKey, <br />		nil],<br />	nil];<br />	<br />	NSViewAnimation* animation = [[NSViewAnimation alloc] initWithViewAnimations:animations];<br />	[animation setDelegate:self];<br />	[animation setAnimationBlockingMode:NSAnimationBlocking];<br />	<br />	viewRemoved = oldView;<br />	[animation startAnimation];<br />	<br />	// animationDidEnd: is called when the animation has finished.<br />
    


    La difficulté est de déterminer oldDestFrame et newDestFrame, qui sont de la même taille que la frame de la vue, mais décalés de frame.size.width pixels sur la droite ou sur la gauche.

    Dans animationDidEnd:, l'ancienne vue est retirée de sa superview, autrement, elle reçoit encore les clics à  son ancienne position d'après ce que j'ai pu constater.

    Si tu as ton bout de code avec CATransitionPush, ça m'intéresse !
  • Nebuchad34Nebuchad34 Membre
    23:33 modifié #28
    dans 1253600295:

    Si tu as ton bout de code avec CATransitionPush, ça m'intéresse !


    Voilà  :
    <br />[containerView setWantsLayer:YES];<br /><br />CATransition *transition = [CATransition animation];<br />[transition setType:kCATransitionPush];<br />[transition setSubtype:kCATransitionFromLeft];<br /><br />NSDictionary *ani = [NSDictionary dictionaryWithObject:transition <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; forKey:@&quot;subviews&quot;];<br /><br />[containerView setAnimations:ani];<br />
    


    Ensuite, il ne reste plus qu'à  faire :

    <br />[[containerView animator] replaceSubview:viewA with:viewB];<br />
    


  • Nebuchad34Nebuchad34 Membre
    septembre 2009 modifié #29
    J'ai utilisé ton code en faisant la chose suivante, car c'est moins gourmand que CATransitonPush finalement !

    L'animation est plus fluide.

    - (void)startAnimationFromCurrentView:(NSView*)view1 toView:(NSView*)view2 direction:(BOOL)leftToRight<br />{<br />	NSViewAnimation* viewAnimation = [[[NSViewAnimation alloc] init] autorelease];<br />	[viewAnimation setAnimationBlockingMode:NSAnimationNonblocking];<br />	[viewAnimation setAnimationCurve:NSAnimationEaseInOut];<br /><br />	[viewAnimation setDelegate:self];<br />	<br />	NSTimeInterval animationDuration = 0.2;<br />	<br />	[viewAnimation setDuration:animationDuration];<br />	<br /><br />	CGRect frameL = CGRectMake(-containerView.bounds.size.width,0,containerView.bounds.size.width,containerView.bounds.size.height);<br />	CGRect frameR = CGRectMake(containerView.bounds.size.width,0,containerView.bounds.size.width,containerView.bounds.size.height);<br />	CGRect frameM = CGRectMake(0,0,containerView.bounds.size.width,containerView.bounds.size.height);<br />	<br />	if (leftToRight)<br />		[view2 setFrame:frameL];<br />	else<br />		[view2 setFrame:frameR];<br /><br />	[view1 resignFirstResponder];<br />	[containerView addSubview:view2];<br />	<br />	<br />	NSDictionary *resizeDictionary;<br />	<br />	if (leftToRight)<br />		resizeDictionary = [NSDictionary dictionaryWithObjectsAndKeys:<br />						&nbsp;  view1, NSViewAnimationTargetKey,<br />						&nbsp;  [NSValue valueWithRect:frameM], NSViewAnimationStartFrameKey,<br />						&nbsp;  [NSValue valueWithRect:frameR] , NSViewAnimationEndFrameKey,<br />						&nbsp;  nil];<br />	else<br />		resizeDictionary = [NSDictionary dictionaryWithObjectsAndKeys:<br />							view1, NSViewAnimationTargetKey,<br />							[NSValue valueWithRect:frameM], NSViewAnimationStartFrameKey,<br />							[NSValue valueWithRect:frameL] , NSViewAnimationEndFrameKey,<br />							nil];<br />	<br />	<br />	NSDictionary *resizeDictionary2 = [NSDictionary dictionaryWithObjectsAndKeys:<br />									&nbsp;  view2, NSViewAnimationTargetKey,<br />									&nbsp;  [NSValue valueWithRect:view2.frame], NSViewAnimationStartFrameKey,<br />									&nbsp;  [NSValue valueWithRect:frameM] , NSViewAnimationEndFrameKey,<br />									&nbsp;  nil];<br />	<br />	NSArray *animationArray;<br />	animationArray = [NSArray arrayWithObjects:<br />					&nbsp; resizeDictionary,<br />					&nbsp; resizeDictionary2,<br />					&nbsp; nil];<br />	<br />	[viewAnimation setViewAnimations:animationArray];<br />	[viewAnimation startAnimation];<br />	<br />	animatingView = view1;<br />	<br />	<br />}<br /><br />- (void)animationDidEnd:(NSAnimation *)animation<br />{<br />	[animatingView removeFromSuperview];<br />	animatingView = nil;<br />}
    
Connectez-vous ou Inscrivez-vous pour répondre.