Comment changer les animations Push et Pop dans une application basée sur la navigation

j'ai une application de navigation et je veux changer l'animation des animations push et pop. Comment dois-je procéder?

202
demandé sur zpasternack 2010-02-07 05:27:50

23 réponses

j'ai fait ce qui suit et ça marche très bien.. et il est simple et facile à comprendre..

CATransition* transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade
//transition.subtype = kCATransitionFromTop; //kCATransitionFromLeft, kCATransitionFromRight, kCATransitionFromTop, kCATransitionFromBottom
[self.navigationController.view.layer addAnimation:transition forKey:nil];
[[self navigationController] popViewControllerAnimated:NO];

Et la même chose pour pousser..


Swift 3.0 version:

let transition = CATransition()
transition.duration = 0.5
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
self.navigationController?.view.layer.add(transition, forKey: nil)
_ = self.navigationController?.popToRootViewController(animated: false)
251
répondu Magnus 2016-07-08 10:53:52

C'est ainsi que j'ai toujours réussi à accomplir cette tâche.

Pour Pousser:

MainView *nextView=[[MainView alloc] init];
[UIView  beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.75];
[self.navigationController pushViewController:nextView animated:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.navigationController.view cache:NO];
[UIView commitAnimations];
[nextView release];

Pour Pop:

[UIView  beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.75];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
[UIView commitAnimations];

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelay:0.375];
[self.navigationController popViewControllerAnimated:NO];
[UIView commitAnimations];



Je reçois encore beaucoup de commentaires à ce sujet, donc je vais aller de l'avant et le mettre à jour pour utiliser des blocs d'animation qui est la façon recommandée par Apple de faire des animations de toute façon.



Pour Push:

MainView *nextView = [[MainView alloc] init];
[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [self.navigationController pushViewController:nextView animated:NO];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.navigationController.view cache:NO];
                         }];

Pour Pop:

[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
                         }];
[self.navigationController popViewControllerAnimated:NO];
256
répondu jordanperry 2014-02-06 19:01:38

push

CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.type = kCATransitionFade;
//transition.subtype = kCATransitionFromTop;

[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController pushViewController:ViewControllerYouWantToPush animated:NO];

pour pop

CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.type = kCATransitionFade;
//transition.subtype = kCATransitionFromTop;

[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController popViewControllerAnimated:NO];
29
répondu Ted 2014-06-23 03:41:28

@Magnus réponse, alors seulement pour Swift (2.0)

    let transition = CATransition()
    transition.duration = 0.5
    transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    transition.type = kCATransitionPush
    transition.subtype = kCATransitionFromTop
    self.navigationController!.view.layer.addAnimation(transition, forKey: nil)
    let writeView : WriteViewController = self.storyboard?.instantiateViewControllerWithIdentifier("WriteView") as! WriteViewController
    self.navigationController?.pushViewController(writeView, animated: false)

Certains sidenotes:

vous pouvez le faire aussi bien avec Segue, il suffit de mettre en œuvre dans prepareForSegue ou shouldPerformSegueWithIdentifier . cependant , cela maintiendra aussi l'animation par défaut. Pour corriger cela, vous devez aller au storyboard, cliquez sur la séquence, et décochez la case "Animates". Mais cela limitera votre application pour IOS 9.0 et au-dessus (au moins quand je l'ai fait dans Xcode 7).

lors d'une séquence, les deux dernières lignes doivent être remplacées par:

self.navigationController?.popViewControllerAnimated(false)

Même si j'ai mis faux, il l'ignore.

17
répondu CularBytes 2015-07-14 11:41:45

rappelez-vous que dans Swift , extension sont certainement vos amis!

public extension UINavigationController {

    /**
     Pop current view controller to previous view controller.

     - parameter type:     transition animation type.
     - parameter duration: transition animation duration.
     */
    func pop(transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.popViewControllerAnimated(false)
    }

    /**
     Push a new view controller on the view controllers's stack.

     - parameter vc:       view controller to push.
     - parameter type:     transition animation type.
     - parameter duration: transition animation duration.
     */
    func push(viewController vc: UIViewController, transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.pushViewController(vc, animated: false)
    }

    private func addTransition(transitionType type: String = kCATransitionFade, duration: CFTimeInterval = 0.3) {
        let transition = CATransition()
        transition.duration = duration
        transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        transition.type = type
        self.view.layer.addAnimation(transition, forKey: nil)
    }

}
13
répondu Luca Davanzo 2016-04-05 14:14:50

Comment changer le Push et Pop des animations dans une navigation en fonction de l'application...

Pour 2018, la "réponse finale!"

Préambule Dire que vous êtes nouveau dans le développement d'iOS, peut-être à partir d'Android. Très déconcertant, Apple fournit deux (2) transitions qui peuvent être utilisées facilement. Ce sont:" crossfade "et"flip". Maintenant, les deux plus inutiles les transitions dans l'ensemble de l'informatique sont "crossfade" et "flip" - que personne n'utilise. Si vous voulez faire les transitions les plus ordinaires, comme "glisser", vous devez faire une énorme quantité de travail. Ce travail, est expliqué dans ce post!

  • tout D'abord, si vous voulez utiliser l'un des deux anime que Apple fournit (crossfade, flips), c'est simple - utilisez la solution de @PeterDeWeese ci-dessus.

  • Secundo, il y a la vieille catransion quickfix. qui est expliqué en détail ici . Il ne fonctionne vraiment pas, et n'est pas une solution réaliste.

sinon, étonnamment, vous devez faire l'effort de faire un pleine transition personnalisée .

à répéter:

même si vous voulez juste la plus simple, la plus commune, transition de déménagement/déménagement , pour le meilleur et pour le pire, vous devez mettre en œuvre une pleine transition personnalisée.

Voici comment faire ...

1. Vous avez besoin d'un personnalisé UIViewControllerAnimatedTransitioning

  1. Vous avez besoin d'un bool de votre propre comme popStyle . (Est-il de sauter sur, ou à éclater?)

  2. vous devez inclure: transitionDuration (trivial) et l'appel principal, animateTransition

  3. en fait, vous aurez besoin d'écrire deux routines différentes pour l'intérieur animateTransition . Un pour le pousser, et un pour le menu. Probablement les nommer animatePush et animatePop . À l'intérieur de animateTransition , juste branche sur popStyle aux deux routines

  4. l'exemple ci-dessous fait un simple déplacement

  5. dans vos routines animatePush et animatePop . Vous devez obtenir le "à vue" et le "à vue". (Comment est montré dans l'exemple.)

  6. et la principale chose que vous devez faire est, en effet, addSubview pour le nouveau " à " vue.

  7. vous devez appeler completeTransition à la fin de votre anime

So ..

  class SimpleOver: NSObject, UIViewControllerAnimatedTransitioning {

        var popStyle: Bool = false

        func transitionDuration(
            using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 0.20
        }

        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

            if popStyle {

                animatePop(using: transitionContext)
                return
            }

            let fz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
            let tz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!

            let f = transitionContext.finalFrame(for: tz)

            let fOff = f.offsetBy(dx: f.width, dy: 55)
            tz.view.frame = fOff

            transitionContext.containerView.insertSubview(tz.view, aboveSubview: fz.view)

            UIView.animate(
                withDuration: transitionDuration(using: transitionContext),
                animations: {
                    tz.view.frame = f
            }, completion: {_ in 
                    transitionContext.completeTransition(true)
            })
        }

        func animatePop(using transitionContext: UIViewControllerContextTransitioning) {

            let fz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
            let tz = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!

            let f = transitionContext.initialFrame(for: fz)
            let fOffPop = f.offsetBy(dx: f.width, dy: 55)

            transitionContext.containerView.insertSubview(tz.view, belowSubview: fz.view)

            UIView.animate(
                withDuration: transitionDuration(using: transitionContext),
                animations: {
                    fz.view.frame = fOffPop
            }, completion: {_ in 
                    transitionContext.completeTransition(true)
            })
        }
    }

et puis ...

2. Utilisez-le dans votre contrôleur de vue.

notez que vous n'avez qu'à faire ce dans le "premier" contrôleur de vue.

celui que vous sautez sur le dessus, ne faites rien. Facile.

C'est votre classe...

class SomeScreen: UIViewController {
}

devient...

class FrontScreen: UIViewController,
        UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {

    let simpleOver = SimpleOver()


    override func viewDidLoad() {

        super.viewDidLoad()
        navigationController?.delegate = self
    }

    func navigationController(
        _ navigationController: UINavigationController,
        animationControllerFor operation: UINavigationControllerOperation,
        from fromVC: UIViewController,
        to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {

        simpleOver.popStyle = (operation == .pop)
        return simpleOver
    }
}

C'est ça.

Poussez et pop exactement comme d'habitude, pas de changement. Pousser. ..

let n = UIStoryboard(name: "nextScreenStoryboardName", bundle: nil)
          .instantiateViewController(withIdentifier: "nextScreenStoryboardID")
          as! NextScreen
navigationController?.pushViewController(n, animated: true)

et pour le pop, vous pouvez si vous voulez juste le faire sur le prochain écran:

class NextScreen: TotallyOrdinaryUIViewController {

    @IBAction func userClickedBackOrDismissOrSomethingLikeThat() {

        navigationController?.popViewController(animated: true)
    }
}

c'est "que simple": O

11
répondu Fattie 2018-03-17 22:31:51

utiliser des appels privés est une mauvaise idée car Apple n'approuve plus les applications qui font cela. Peut-être pourriez-vous essayer ceci:

//Init Animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.50];


[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.navigationController.view cache:YES];

//Create ViewController
MyViewController *myVC = [[MyViewController alloc] initWith...];

[self.navigationController pushViewController:myVC animated:NO];
[myVC release];

//Start Animation
[UIView commitAnimations];
10
répondu nicktmro 2010-03-27 23:03:45

étant donné que C'est le meilleur résultat sur Google, j'ai pensé partager ce que je pense être la manière la plus saine; qui est d'utiliser l'API de transition iOS 7+. J'ai implémenté ceci pour iOS 10 avec Swift 3.

il est assez simple de combiner ceci avec comment UINavigationController s'Anime entre deux contrôleurs de vue si vous créez une sous-classe de UINavigationController et retournez une instance d'une classe qui est conforme au protocole UIViewControllerAnimatedTransitioning .

par exemple voici mon UINavigationController sous-classe:

class NavigationController: UINavigationController {
    init() {
        super.init(nibName: nil, bundle: nil)

        delegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension NavigationController: UINavigationControllerDelegate {

    public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return NavigationControllerAnimation(operation: operation)
    }

}

vous pouvez voir que j'ai réglé le UINavigationControllerDelegate à lui-même, et dans une extension sur ma sous-classe j'implémente la méthode dans UINavigationControllerDelegate qui vous permet de retourner un contrôleur d'animation personnalisé (i.e., NavigationControllerAnimation ). Ce contrôleur d'animation personnalisé remplacera l'animation stock pour vous.

vous vous demandez probablement pourquoi je passe dans l'opération à l'instance NavigationControllerAnimation via son initialiseur. Je le fais donc que dans NavigationControllerAnimation l 'application du protocole UIViewControllerAnimatedTransitioning je sais ce que l'opération est (i.e., "push" ou "pop"). Cela aide à savoir quel type d'animation je dois faire. La plupart du temps, vous souhaitez effectuer une animation différente selon le type d'opération.

le reste est assez standard. Implémentez les deux fonctions requises dans le protocole UIViewControllerAnimatedTransitioning et animez comme vous voulez:

class NavigationControllerAnimation: NSObject, UIViewControllerAnimatedTransitioning {

    let operation: UINavigationControllerOperation

    init(operation: UINavigationControllerOperation) {
        self.operation = operation

        super.init()
    }

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }

    public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
            let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
        let containerView = transitionContext.containerView

        if operation == .push {
            // do your animation for push
        } else if operation == .pop {
            // do your animation for pop
        }
    }
}

il est important de se rappeler, que pour chaque type d'opération (c'est-à-dire "push" ou "pop"), les contrôleurs de vue to ET from seront différents. Lorsque vous êtes dans une opération de poussée, le contrôleur to view est celui qui est poussé. Lorsque vous êtes dans un menu de l'opération, la de-vue-contrôleur sera celle qui est passée à, et le de-vue-contrôleur sera celui qui est sauté.

de plus, le contrôleur de vue to doit être ajouté comme sous-vue du containerView dans le contexte de transition.

lorsque votre animation est terminée, vous devez appeler transitionContext.completeTransition(true) . Si vous effectuez une transition interactive, vous devrez retourner dynamiquement un Bool à completeTransition(didComplete: Bool) , selon que la transition est terminée à la fin de l'animation.

Enfin facultatif ), vous pouvez voir comment j'ai fait la transition, j'ai été travailler sur. Ce code est un peu plus hacky et je l'ai écrit assez rapidement donc je ne dirais pas que c'est un super code d'animation mais ça montre quand même comment faire la partie animation.

Mine était une transition vraiment simple; je voulais imiter la même animation que UINavigationController fait habituellement, mais au lieu de la 'page suivante au-dessus de l'animation supérieure' qu'il fait, je voulais implémenter une animation 1:1 de l'ancien contrôleur de vue loin en même temps que le nouveau contrôleur de vue apparaît. Cela a pour effet de rendre les deux contrôleurs de vue on dirait qu'ils sont épinglés l'un à l'autre.

pour l'opération de poussée, qui nécessite de régler d'abord l'origine de la vue toViewController sur l'écran X–axis off, en l'ajoutant comme sous-vue du containerView , en l'animant sur l'écran en réglant ce origin.x à zéro. En même temps, j'anime la vue du fromViewController en mettant son origin.x hors de l'écran:

toViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.size.width, dy: 0.0)

containerView.addSubview(toViewController.view)

UIView.animate(withDuration: transitionDuration(using: transitionContext),
               delay: 0,
               options: [ UIViewAnimationOptions.curveEaseOut ],
               animations: {
                toViewController.view.frame = containerView.bounds
                fromViewController.view.frame = containerView.bounds.offsetBy(dx: -containerView.frame.size.width, dy: 0)
},
               completion: { (finished) in
                transitionContext.completeTransition(true)
})

l'opération pop est essentiellement l'inverse. Ajouter le toViewController comme sous-vue du containerView , et animer loin le fromViewController à droite comme vous animer dans le toViewController à partir de la gauche:

containerView.addSubview(toViewController.view)

UIView.animate(withDuration: transitionDuration(using: transitionContext),
               delay: 0,
               options: [ UIViewAnimationOptions.curveEaseOut ],
               animations: {
                fromViewController.view.frame = containerView.bounds.offsetBy(dx: containerView.frame.width, dy: 0)
                toViewController.view.frame = containerView.bounds
},
               completion: { (finished) in
                transitionContext.completeTransition(true)
})

voici un gist avec l'ensemble du fichier swift:

https://gist.github.com/alanzeino/603293f9da5cd0b7f6b60dc20bc766be

8
répondu Alan Zeino 2017-01-08 00:12:47

Il ya UINavigationControllerDelegate et UIViewControllerAnimatedTransitioning il vous pouvez changer l'animation pour tout ce que vous voulez.

par exemple c'est de l'animation pop verticale pour VC:

@objc class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning {

func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
    return 0.5
}

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {

    let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
    let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
    let containerView = transitionContext.containerView()
    let bounds = UIScreen.mainScreen().bounds
    containerView!.insertSubview(toViewController.view, belowSubview: fromViewController.view)
    toViewController.view.alpha = 0.5

    let finalFrameForVC = fromViewController.view.frame

    UIView.animateWithDuration(transitionDuration(transitionContext), animations: {
        fromViewController.view.frame = CGRectOffset(finalFrameForVC, 0, bounds.height)
        toViewController.view.alpha = 1.0
        }, completion: {
            finished in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
    })
}

}

et ensuite

func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if operation == .Pop {
        return PopAnimator()
    }
    return nil;
}

tutoriel utile https://www.objc.io/issues/5-ios7/view-controller-transitions /

6
répondu eilas 2015-12-30 15:05:34

j'ai récemment essayé de faire quelque chose de similaire. J'ai décidé que je n'aimais pas l'animation coulissante du contrôleur D'UINavigationController, mais je ne voulais pas non plus faire les animations Qu'UIView vous donne comme curl ou quelque chose comme ça. Je voulais faire un fondu croisé entre les vues quand je poussais ou pop.

le problème là-bas implique le fait que la vue est littéralement supprimer la vue ou sautant un sur le dessus de l'actuelle, de sorte qu'un fondu ne fonctionne pas. Le la solution que j'ai trouvée consiste à prendre ma nouvelle vue et à l'ajouter comme sous-vue à la vue supérieure actuelle sur la pile UIViewController. Je l'ajoute avec un alpha de 0, puis je fais un crossfade. Lorsque la séquence d'animation finitions, je pousse la vue sur la pile sans l'animer. Je retourne ensuite à l'ancien topView et je nettoie les choses que j'avais changées.

C'est un peu plus compliqué que cela, parce que vous avez les navigationItems que vous devez ajuster pour faire la transition regarde correctement. En outre, si vous faites n'importe quelle rotation, vous devez alors ajuster les tailles de cadre que vous ajoutez les vues en sous-vues pour qu'elles apparaissent correctement à l'écran. Voici une partie du code que j'ai utilisé. J'ai subclassé le contrôleur D'Uinavigationet j'ai surpassé les méthodes push et pop.

-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
      UIViewController *currentViewController = [self.viewControllers lastObject];
      //if we don't have a current controller, we just do a normal push
      if(currentViewController == nil)
      {
         [super pushViewController:viewController animated:animated];
         return;
      }
      //if no animation was requested, we can skip the cross fade
      if(!animation)
      {
         [super pushViewController:viewController animated:NO];
         return;
      }
      //start the cross fade.  This is a tricky thing.  We basically add the new view
//as a subview of the current view, and do a cross fade through alpha values.
//then we push the new view on the stack without animating it, so it seemlessly is there.
//Finally we remove the new view that was added as a subview to the current view.

viewController.view.alpha = 0.0;
//we need to hold onto this value, we'll be releasing it later
    NSString *title = [currentViewController.title retain];

//add the view as a subview of the current view
[currentViewController.view addSubview:viewController.view];
[currentViewController.view bringSubviewToFront:viewController.view];
UIBarButtonItem *rButtonItem = currentViewController.navigationItem.rightBarButtonItem;
UIBarButtonItem *lButtonItem = currentViewController.navigationItem.leftBarButtonItem;

NSArray *array = nil;

//if we have a right bar button, we need to add it to the array, if not, we will crash when we try and assign it
//so leave it out of the array we are creating to pass as the context.  I always have a left bar button, so I'm not checking to see if it is nil. Its a little sloppy, but you may want to be checking for the left BarButtonItem as well.
if(rButtonItem != nil)
    array = [[NSArray alloc] initWithObjects:currentViewController,viewController,title,lButtonItem,rButtonItem,nil];
else {
    array = [[NSArray alloc] initWithObjects:currentViewController,viewController,title,lButtonItem,nil];
}

//remove the right bar button for our transition
[currentViewController.navigationItem setRightBarButtonItem:nil animated:YES];
//remove the left bar button and create a backbarbutton looking item
//[currentViewController.navigationItem setLeftBarButtonItem:nil animated:NO];

//set the back button
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:title style:kButtonStyle target:self action:@selector(goBack)];
[currentViewController.navigationItem setLeftBarButtonItem:backButton animated:YES];
[viewController.navigationItem setLeftBarButtonItem:backButton animated:NO];
[backButton release];

[currentViewController setTitle:viewController.title];

[UIView beginAnimations:@"push view" context:array];
[UIView setAnimationDidStopSelector:@selector(animationForCrossFadePushDidStop:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.80];
[viewController.view setAlpha: 1.0];
[UIView commitAnimations];
}

-(void)animationForCrossFadePushDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{

UIViewController *c = [(NSArray*)context objectAtIndex:0];
UIViewController *n = [(NSArray*)context objectAtIndex:1];
NSString *title     = [(NSArray *)context objectAtIndex:2];
UIBarButtonItem *l = [(NSArray *)context objectAtIndex:3];
UIBarButtonItem *r = nil;
//not all views have a right bar button, if we look for it and it isn't in the context,
//we'll crash out and not complete the method, but the program won't crash.
//So, we need to check if it is there and skip it if it isn't.
if([(NSArray *)context count] == 5)
    r = [(NSArray *)context objectAtIndex:4];

//Take the new view away from being a subview of the current view so when we go back to it
//it won't be there anymore.
[[[c.view subviews] lastObject] removeFromSuperview];
[c setTitle:title];
[title release];
//set the search button
[c.navigationItem setLeftBarButtonItem:l animated:NO];
//set the next button
if(r != nil)
    [c.navigationItem setRightBarButtonItem:r animated:NO];


[super pushViewController:n animated:NO];

 }

comme je le mentionne dans le code, j'ai toujours un élément du bouton gauche de la barre, donc je ne vérifie pas s'il est nul avant de le mettre dans le tableau que je passe comme contexte pour l'animation delegate. Si vous faites cela, vous pouvez faire cette vérification.

le problème que j'ai trouvé était que si vous vous écrasez du tout dans la méthode delegate, il ne va pas planter le programme. Ça empêche le délégué de terminer, mais on n'a pas d'avertissement.

Donc depuis que je faisais mon nettoyage dans cette routine de délégué, ça causait un comportement visuel bizarre car ça ne finissait pas le nettoyage.

le bouton arrière que je crée appelle un "goBack"" méthode, et cette méthode appelle juste la routine pop.

-(void)goBack
{ 
     [self popViewControllerAnimated:YES];
}

aussi, voici ma routine pop.

-(UIViewController *)popViewControllerAnimated:(BOOL)animated
{
    //get the count for the number of viewControllers on the stack
int viewCount = [[self viewControllers] count];
//get the top view controller on the stack
UIViewController *topViewController = [self.viewControllers objectAtIndex:viewCount - 1];
//get the next viewController after the top one (this will be the new top one)
UIViewController *newTopViewController = [self.viewControllers objectAtIndex:viewCount - 2];

//if no animation was requested, we can skip the cross fade
if(!animated)
{
    [super popViewControllerAnimated:NO];
            return topViewController;
}



//start of the cross fade pop.  A bit tricky.  We need to add the new top controller
//as a subview of the curent view controler with an alpha of 0.  We then do a cross fade.
//After that we pop the view controller off the stack without animating it.
//Then the cleanup happens: if the view that was popped is not released, then we
//need to remove the subview we added and change some titles back.
newTopViewController.view.alpha = 0.0;
[topViewController.view addSubview:newTopViewController.view];
[topViewController.view bringSubviewToFront:newTopViewController.view];
NSString *title = [topViewController.title retain];
UIBarButtonItem *lButtonItem = topViewController.navigationItem.leftBarButtonItem;
UIBarButtonItem *rButtonItem = topViewController.navigationItem.rightBarButtonItem;

//set the new buttons on top of the current controller from the new top controller
if(newTopViewController.navigationItem.leftBarButtonItem != nil)
{
    [topViewController.navigationItem setLeftBarButtonItem:newTopViewController.navigationItem.leftBarButtonItem animated:YES];
}
if(newTopViewController.navigationItem.rightBarButtonItem != nil)
{
    [topViewController.navigationItem setRightBarButtonItem:newTopViewController.navigationItem.rightBarButtonItem animated:YES];
}

[topViewController setTitle:newTopViewController.title];
//[topViewController.navigationItem.leftBarButtonItem setTitle:newTopViewController.navigationItem.leftBarButtonItem.title];

NSArray *array = nil;
if(rButtonItem != nil)
    array = [[NSArray alloc] initWithObjects:topViewController,title,lButtonItem,rButtonItem,nil];
else {
    array = [[NSArray alloc] initWithObjects:topViewController,title,lButtonItem,nil];
}


[UIView beginAnimations:@"pop view" context:array];
[UIView setAnimationDidStopSelector:@selector(animationForCrossFadePopDidStop:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.80];
[newTopViewController.view setAlpha: 1.0];
[UIView commitAnimations];
return topViewController;

 }

 -(void)animationForCrossFadePopDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
 {

UIViewController *c = [(NSArray *)context objectAtIndex:0];
//UIViewController *n = [(NSArray *)context objectAtIndex:1];
NSString *title = [(NSArray *)context objectAtIndex:1];
UIBarButtonItem *l = [(NSArray *)context objectAtIndex:2];
UIBarButtonItem *r = nil;



//Not all views have a right bar button.  If we look for one that isn't there
// we'll crash out and not complete this method, but the program will continue.
//So we need to check if it is therea nd skip it if it isn't.
if([(NSArray *)context count] == 4)
    r = [(NSArray *)context objectAtIndex:3];

//pop the current view from the stack without animation
[super popViewControllerAnimated:NO];

//if what was the current veiw controller is not nil, then lets correct the changes
//we made to it.
if(c != nil)
{
    //remove the subview we added for the transition
    [[c.view.subviews lastObject] removeFromSuperview];
    //reset the title we changed
    c.title = title;
    [title release];
    //replace the left bar button that we changed
    [c.navigationItem setLeftBarButtonItem:l animated:NO];
    //if we were passed a right bar button item, replace that one as well
    if(r != nil)
        [c.navigationItem setRightBarButtonItem:r animated:NO];
    else {
        [c.navigationItem setRightBarButtonItem:nil animated:NO];
    }


 }
}

c'est à peu près tout. Vous aurez besoin d'un peu de code supplémentaire si vous voulez implémenter des rotations. Vous aurez besoin de définir la taille de cadre de vos vues que vous ajoutez sous forme de sous-vues avant de les montrer sinon vous allez rencontrer des problèmes l'orientation est le paysage, mais la dernière fois que vous avez vu la vue précédente c'était portrait. Donc, vous l'ajouter comme une vue secondaire et le fade in mais il apparaît comme portrait, puis quand nous pop sans animation, la même vue, mais celle qui est dans la pile, est maintenant paysage. Tout ça a l'air un peu bizarre. La mise en œuvre de la rotation est un peu différente pour tout le monde, donc je n'ai pas inclus mon code pour ça ici.

J'espère que ça aidera certaines personnes. J'ai cherché partout quelque chose comme ça et je n'ai rien trouvé. Je ne pense pas que ce soit la réponse parfaite, mais ça marche vraiment. bien pour moi à ce point.

4
répondu georryan 2011-03-25 18:19:09

Voici comment J'ai fait la même chose à Swift:

Pour Pousser:

    UIView.animateWithDuration(0.75, animations: { () -> Void in
        UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut)
        self.navigationController!.pushViewController(nextView, animated: false)
        UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromRight, forView: self.navigationController!.view!, cache: false)
    })

Pour De La Pop:

en fait, j'ai fait cela un peu différemment de certaines des réponses ci - dessus-mais comme je suis nouveau dans le développement Swift, cela pourrait ne pas être juste. J'ai remplacé viewWillDisappear:animated: et y ai ajouté le code pop:

    UIView.animateWithDuration(0.75, animations: { () -> Void in
        UIView.setAnimationCurve(UIViewAnimationCurve.EaseInOut)
        UIView.setAnimationTransition(UIViewAnimationTransition.FlipFromLeft, forView: self.navigationController!.view, cache: false)
    })

    super.viewWillDisappear(animated)
4
répondu djbp 2015-01-20 11:22:46

basé sur jordanperry réponse mise à jour pour swift 4

push UIViewController

let yourVC = self.storyboard?.instantiateViewController(withIdentifier: "yourViewController") as! yourViewController
    UIView.animate(withDuration: 0.75, animations: {() -> Void in
    UIView.setAnimationCurve(.easeInOut)
    self.navigationController?.pushViewController(terms, animated: true)
    UIView.setAnimationTransition(.flipFromRight, for: (self.navigationController?.view)!, cache: false)
})

Pour Pop

UIView.animate(withDuration: 0.75, animations: {() -> Void in
    UIView.setAnimationCurve(.easeInOut)
    UIView.setAnimationTransition(.flipFromLeft, for: (self.navigationController?.view)!, cache: false)
})
navigationController?.popViewController(animated: false)
4
répondu vp2698 2018-03-26 09:02:48

en utilisant la réponse d'iJordan comme source d'inspiration, pourquoi ne pas simplement créer une catégorie sur UINavigationController à utiliser dans toute votre application au lieu de copier/coller ce code d'animation partout?

UINavigationController+Animation.h

@interface UINavigationController (Animation)

- (void) pushViewControllerWithFlip:(UIViewController*) controller;

- (void) popViewControllerWithFlip;

@end

UINavigationController+Animation.m

@implementation UINavigationController (Animation)

- (void) pushViewControllerWithFlip:(UIViewController *) controller
{
    [UIView animateWithDuration:0.50
                     animations:^{
                         [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                         [self pushViewController:controller animated:NO];
                         [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO];
                     }];
}

- (void) popViewControllerWithFlip
{
    [UIView animateWithDuration:0.5
                     animations:^{
                         [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                         [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:NO];
                     }];

    [self popViewControllerAnimated:NO];
}

@end

ensuite il suffit d'importer L'Animation UINavigationController+.h classer et appeler normalement:

[self.navigationController pushViewControllerWithFlip:[[NewViewController alloc] init]];

[self.navigationController popViewControllerWithFlip];
3
répondu DiscDev 2013-11-11 16:16:29

regardez ADTransitionController , un remplacement pour UINavigationController avec des animations de transition personnalisées (son API correspond à L'API de UINavigationController) que nous avons créé chez Applidium.

vous pouvez utiliser différentes animations prédéfinies pour push et pop actions telles que Swipe , Fade , Cube , Carrousel , Zoom et ainsi de suite.

2
répondu felginep 2013-07-17 13:58:06

alors que toutes les réponses ici sont grandes et la plupart fonctionnent très bien, il y a une méthode légèrement plus simple qui atteint le même effet...

Pour Pousser:

  NextViewController *nextViewController = [[NextViewController alloc] init];

  // Shift the view to take the status bar into account 
  CGRect frame = nextViewController.view.frame;
  frame.origin.y -= 20;
  frame.size.height += 20;
  nextViewController.view.frame = frame;

  [UIView transitionFromView:self.navigationController.topViewController.view toView:nextViewController.view duration:0.5 options:UIViewAnimationOptionTransitionFlipFromRight completion:^(BOOL finished) {
    [self.navigationController pushViewController:nextViewController animated:NO];
  }];

Pour Pop:

  int numViewControllers = self.navigationController.viewControllers.count;
  UIView *nextView = [[self.navigationController.viewControllers objectAtIndex:numViewControllers - 2] view];

  [UIView transitionFromView:self.navigationController.topViewController.view toView:nextView duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
    [self.navigationController popViewControllerAnimated:NO];
  }];}
2
répondu head in the codes 2014-05-01 19:49:57

Vous pouvez maintenant utiliser UIView.transition . Notez que animated:false . Cela fonctionne avec n'importe quelle option de transition, pop, push ou stack remplacer.

if let nav = self.navigationController
{
    UIView.transition(with:nav.view, duration:0.3, options:.transitionCrossDissolve, animations: {
        _ = nav.popViewController(animated:false)
    }, completion:nil)
}
2
répondu Peter DeWeese 2017-04-27 12:18:06

c'est très simple

self.navigationController?.view.semanticContentAttribute = .forceRightToLeft
2
répondu user2501116 2018-03-13 08:21:27

Voir ma réponse à cette question pour un moyen de le faire en beaucoup moins de lignes de code. Cette méthode vous permet d'animer un pseudo - "Push" d'un nouveau contrôleur de vue de n'importe quelle façon que vous voulez, et quand l'animation est faite il met en place le contrôleur de Navigation comme si vous aviez utilisé la méthode Standard Push. Mon exemple permet d'animer une diapositive à partir de la gauche ou de la droite. Code répété ici pour plus de commodité:

-(void) showVC:(UIViewController *) nextVC rightToLeft:(BOOL) rightToLeft {
    [self addChildViewController:neighbor];
    CGRect offscreenFrame = self.view.frame;
    if(rightToLeft) {
        offscreenFrame.origin.x = offscreenFrame.size.width * -1.0;
    } else if(direction == MyClimbDirectionRight) {
        offscreenFrame.origin.x = offscreenFrame.size.width;
    }
    [[neighbor view] setFrame:offscreenFrame];
    [self.view addSubview:[neighbor view]];
    [neighbor didMoveToParentViewController:self];
    [UIView animateWithDuration:0.5 animations:^{
        [[neighbor view] setFrame:self.view.frame];
    } completion:^(BOOL finished){
        [neighbor willMoveToParentViewController:nil];
        [neighbor.view removeFromSuperview];
        [neighbor removeFromParentViewController];
        [[self navigationController] pushViewController:neighbor animated:NO];
        NSMutableArray *newStack = [[[self navigationController] viewControllers] mutableCopy];
        [newStack removeObjectAtIndex:1]; //self, just below top
        [[self navigationController] setViewControllers:newStack];
    }];
}
1
répondu RobP 2017-05-23 12:26:25

Je ne sais pas comment vous pouvez changer l'animation de transition publiquement.

si le bouton" Retour "n'est pas nécessaire, vous devriez utiliser contrôleurs de vue modale pour avoir les transitions" push from bottom " / "flip" / "fade" / (≥3.2)" page curl".


sur le privé côté, la méthode -pushViewController:animated: appelle le méthode non documentée -pushViewController:transition:forceImmediate: , donc par exemple si vous voulez une transition de gauche à droite, vous pouvez utiliser

[navCtrler pushViewController:ctrler transition:10 forceImmediate:NO];

vous ne pouvez pas changer la transition" pop " de cette façon, cependant.

0
répondu kennytm 2017-05-23 12:26:25

de l'application d'échantillonnage, Vérifiez cette variation. https://github.com/mpospese/MPFoldTransition /

#pragma mark - UINavigationController(MPFoldTransition)

@implementation UINavigationController(MPFoldTransition)

//- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
- (void)pushViewController:(UIViewController *)viewController foldStyle:(MPFoldStyle)style
{
    [MPFoldTransition transitionFromViewController:[self visibleViewController] 
                                  toViewController:viewController 
                                          duration:[MPFoldTransition defaultDuration]  
                                             style:style 
                                        completion:^(BOOL finished) {
                                            [self pushViewController:viewController animated:NO];
                                        }
     ];
}

- (UIViewController *)popViewControllerWithFoldStyle:(MPFoldStyle)style
{
    UIViewController *toController = [[self viewControllers] objectAtIndex:[[self viewControllers] count] - 2];

    [MPFoldTransition transitionFromViewController:[self visibleViewController] 
                                  toViewController:toController 
                                          duration:[MPFoldTransition defaultDuration] 
                                             style:style
                                        completion:^(BOOL finished) {
                                            [self popViewControllerAnimated:NO];
                                        }
     ];

    return toController;
}
0
répondu johndpope 2012-07-14 04:37:12

il suffit d'utiliser:

ViewController *viewController = [[ViewController alloc] init];

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
navController.navigationBarHidden = YES;

[self presentViewController:navController animated:YES completion: nil];
[viewController release];
[navController release];
0
répondu Sergio Andreotti 2013-04-23 15:43:18

réalisant que c'est une vieille question. Je voudrais encore poster cette réponse, car j'ai eu quelques problèmes popping plusieurs viewControllers avec les réponses proposées. Ma solution est de sous-classe UINavigationController et de remplacer tous les pop et les méthodes push.

FlippingNavigationController.h

@interface FlippingNavigationController : UINavigationController

@end

FlippingNavigationController.m:

#import "FlippingNavigationController.h"

#define FLIP_DURATION 0.5

@implementation FlippingNavigationController

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    [UIView transitionWithView:self.view
                      duration:animated?FLIP_DURATION:0
                       options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromRight
                    animations:^{ [super pushViewController:viewController
                                                   animated:NO]; }
                    completion:nil];
}

- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
    return [[self popToViewController:[self.viewControllers[self.viewControllers.count - 2]]
                             animated:animated] lastObject];
}

- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
{
    return [self popToViewController:[self.viewControllers firstObject]
                            animated:animated];
}

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    __block NSArray* viewControllers = nil;

    [UIView transitionWithView:self.view
                      duration:animated?FLIP_DURATION:0
                       options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionFlipFromLeft
                    animations:^{ viewControllers = [super popToViewController:viewController animated:NO]; }
                    completion:nil];

    return viewControllers;
}

@end
0
répondu Christian Otkjær 2015-06-30 13:31:47

j'ai trouvé un moyen légèrement récursif de faire ceci qui fonctionne à mes fins. J'ai une variable BOOL d'instance que j'utilise pour bloquer l'animation normale et remplacer mon propre message pop non animé. La variable est initialement définie à NO. Lorsque le bouton back est activé, la méthode delegate le positionne à YES et envoie un nouveau message pop non animé à la barre de navigation, appelant ainsi à nouveau la même méthode delegate, cette fois avec la variable définie à YES. Avec la variable est définie à Oui, la méthode delegate la règle à non et renvoie Oui pour permettre à la pop non animée de se produire. Après le retour du second appel de delegate, nous nous retrouvons dans le premier, où NO est retourné, bloquant la pop animée originale! Il n'est effectivement pas aussi bordélique que ça sonne. Ma méthode shouldPopItem ressemble à ceci:

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item 
{
    if ([[navigationBar items] indexOfObject:item] == 1) 
    {
        [expandedStack restack];    
    }

    if (!progPop) 
    {
        progPop = YES;
        [navBar popNavigationItemAnimated:NO];
        return NO;
    }
    else 
    {
        progPop = NO;
        return YES;
    }
}

ça me va.

-1
répondu CharlieMezak 2016-06-23 05:37:04