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

221

J'ai une application basée sur la navigation et je veux changer l'animation des animations push et pop. Comment ferais-je ça?

Modifier 2018

Il y a eu beaucoup de réponses à cette question et cela fait un bon moment maintenant, j'ai re-choisi la réponse à ce que je pense être le plus pertinent maintenant. S'il y a quelqu'un qui pense le contraire, veuillez me le faire savoir dans les commentaires

Coup
la source
25
Depuis iOS 7, il existe une API officielle pour cela; voir la prise en charge de l'animation de transition personnalisée d' UINavigationControllerDelegate . Il y a aussi une vidéo WWDC 2013 à ce sujet.
Jesse Rusak
J'ai ajouté une réponse (ci-dessous) pour avoir fait cela dans Swift - je suis tombé sur cette question demandant des implémentations de Swift alors j'ai pensé que je ferais écho à mon implémentation suivante.
djbp
1
Pour un très bon tutoriel avec l'API officielle (iOS 7+), voir: bradbambara.wordpress.com/2014/04/11/…
nikolovski
1
@JesseRusak a mis à jour le lien vers la vidéo de la WWDC 2013: developer.apple.com/videos/play/wwdc2013-218
Wojciech Rutkowski
1
J'ai changé ma réponse acceptée les gars et les filles. J'espère que cela t'aides! GLHF
Jab

Réponses:

35

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

Pour 2019, la "réponse finale!"

Préambule:

Disons que vous êtes nouveau dans le développement iOS. Confusément, Apple propose deux transitions qui peuvent être utilisées facilement. Ce sont: "crossfade" et "flip".

Mais bien sûr, "crossfade" et "flip" sont inutiles. Ils ne sont jamais utilisés. Personne ne sait pourquoi Apple a fourni ces deux transitions inutiles!

Alors:

Supposons que vous souhaitiez effectuer une transition ordinaire et courante, telle que "diapositive". Dans ce cas, vous devez faire une énorme quantité de travail! .

Ce travail est expliqué dans cet article.

Juste pour répéter:

Étonnamment: avec iOS, si vous voulez les transitions quotidiennes les plus simples et les plus courantes (comme une diapositive ordinaire), vous devez effectuer tout le travail de mise en œuvre d'une transition personnalisée complète .

Voici comment procéder ...

1. Vous avez besoin d'une coutume UIViewControllerAnimatedTransitioning

  1. Vous avez besoin d'un bool de votre choix popStyle. (Est-ce qu'il saute ou saute?)

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

  3. En fait, vous devez écrire deux routines différentes pour l'intérieur animateTransition. Un pour le push et un pour la pop. Nommez-les probablement animatePushet animatePop. À l'intérieur animateTransition, branchez -vous simplement sur popStyleles deux routines

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

  5. Dans vos routines animatePushet animatePop. Vous devez obtenir le "de la vue" et le "voir". (Comment faire, est indiqué dans l'exemple de code.)

  6. et vous devez addSubview pour la nouvelle vue "à".

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

Alors ..

  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)
            })
        }
    }

Puis ...

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

Remarque: étrangement, vous n'avez qu'à le faire dans le "premier" contrôleur de vue. (Celui qui est "en dessous".)

Avec celui que vous sautez dessus , ne faites rien . Facile.

Alors ta 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 tout.

Poussez et éclatez exactement comme d'habitude, aucun changement. Pousser ...

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

et pour le faire apparaître, vous pouvez le faire si vous le souhaitez sur l'écran suivant:

class NextScreen: TotallyOrdinaryUIViewController {
    
    @IBAction func userClickedBackOrDismissOrSomethingLikeThat() {
        
        navigationController?.popViewController(animated: true)
    }
}

Phew.


3. Profitez également des autres réponses de cette page qui expliquent comment remplacer AnimatedTransitioning

Faites défiler jusqu'à @AlanZeino et @elias answer pour plus de discussion sur la façon d'utiliser AnimatedTransitioningles applications iOS de nos jours!

Fattie
la source
Excellent! Si je veux que le geste de balayage arrière de navigation prenne également en charge la même transition animée. Une idée?
sam chi wen
merci @samchiwen - en effet c'est exactement ce animatePushque animatePopsont et .. les deux directions différentes!
Fattie
268

J'ai fait ce qui suit et cela fonctionne bien .. et 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 push ..


Version Swift 3.0:

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)
Magnus
la source
34
+1, c'est vraiment la solution la plus saine. Juste une petite note pour les futurs visiteurs: la Animated:NOpièce est vitale. Si YESc'est réussi, les animations se mélangent et provoquent des effets amusants.
DarkDust
12
La meilleure solution jusqu'à présent .. Et pour les débutants, n'oubliez pas d'inclure QuartCore (#import <QuartzCore / QuartzCore.h>)
nomann
4
Le seul problème que j'ai avec cette solution est que le viewDidAppear du viewcontroller poussé soit appelé immédiatement après l'avoir poussé sans animation. Y a-t-il un moyen de contourner cela?
Pedro Mancheno
9
Mon problème avec ce code est que chaque vue semble clignoter en gris ou blanc lorsqu'elles glissent vers l'intérieur ou vers l'extérieur.
Chris
1
vérifié sur iOS 7.1.2 et iOS 8.3 - ce code fonctionne très bien également pour la méthodesetViewControllers:
proff
256

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

Pour Push:

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 la 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];


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

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 la pop:

[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
                         }];
[self.navigationController popViewControllerAnimated:NO];
jordanperry
la source
3
Merci pour cela. Mais le pop est fait automatiquement par l'UINavigationController. Comment remplacez-vous ce comportement pour pouvoir appeler votre logique pop personnalisée?
Joshua Frank
1
@stuckj en fait ça marche !!! il vous suffit de remplacer superparself.navigationController
holierthanthou84
Existe-t-il un moyen d'obtenir une diapositive à partir de la gauche au lieu de la diapositive par défaut à partir de la droite?
shim
Le premier ne montre pas du tout la nouvelle vue. Le second ne montre pas d'animation. Très mauvaise réponse! iOS 7.
Dmitry
2
pourquoi avez-vous donné la UIViewControllersous-classe d'un nom sans partie "ViewController". Ce nom est plus approprié pour UIView.
user2159978
29

pour pousser

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 la 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];
Ted
la source
19

@Magnus répond, 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)

Quelques notes:

Vous pouvez également le faire avec Segue, implémentez-le simplement dans prepareForSegueou shouldPerformSegueWithIdentifier. Cependant , cela conservera également l'animation par défaut. Pour résoudre ce problème, vous devez vous rendre dans le storyboard, cliquez sur la séquence et décochez la case «Animations». Mais cela limitera votre application pour IOS 9.0 et supérieur (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 je mets false, cela l'ignore en quelque sorte.

CularBytes
la source
Comment supprimer la couleur noire en arrière-plan à la fin de l'animation.
Madhu
Ne fonctionne pas pour l'animation du contrôleur de vue push fonctionne pour le contrôleur de vue Pop
Mukul More
16

N'oubliez pas que dans Swift , l' extension est définitivement 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)
    }

}
Luca Davanzo
la source
11

L'utilisation d'appels privés est une mauvaise idée car Apple n'approuve plus les applications qui le font. Vous pourriez peut-être 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];
nicktmro
la source
Cela ne fonctionne que "à moitié" - cela ne résoudra pas le problème plus difficile des animations pop.
Adam
J'aime mieux cette solution, et oui ça marche. L'utilisation de méthodes privées vous fera rejeter à coup sûr.
Benjamin Intal
@nicktmro qui est l'appel api privé. Je n'en ai pas remarqué.
Franklin
@Franklin, il y a eu une discussion ici il y a quelque temps sur l'utilisation, -pushViewController:transition:forceImmediate:ce qui serait une mauvaise idée.
nicktmro
9

É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 sensée; qui consiste à utiliser l'API de transition iOS 7+. J'ai implémenté cela pour iOS 10 avec Swift 3.

Il est assez simple de combiner cela avec la façon dont UINavigationControllers'anime entre deux contrôleurs de vue si vous créez une sous-classe de UINavigationControlleret renvoyez une instance d'une classe conforme au UIViewControllerAnimatedTransitioningprotocole.

Par exemple, voici ma UINavigationControllersous - 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 je mets le UINavigationControllerDelegateà lui-même, et dans une extension de ma sous-classe, j'implémente la méthode UINavigationControllerDelegatequi vous permet de retourner un contrôleur d'animation personnalisé (c'est-à-dire NavigationControllerAnimation). Ce contrôleur d'animation personnalisé remplacera l'animation stock pour vous.

Vous vous demandez probablement pourquoi je passe l'opération à l' NavigationControllerAnimationinstance via son initialiseur. Je le fais pour que dans NavigationControllerAnimationla mise en œuvre du UIViewControllerAnimatedTransitioningprotocole, je sache quelle est l'opération (c'est-à-dire «pousser» ou «pop»). Cela permet de savoir quel type d'animation je dois faire. La plupart du temps, vous souhaitez effectuer une animation différente en fonction de l'opération.

Le reste est assez standard. Implémentez les deux fonctions requises dans le UIViewControllerAnimatedTransitioningprotocole et animez comme vous le souhaitez:

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 différent (c'est-à-dire «pousser» ou «pop»), les contrôleurs de vue vers et depuis seront différents. Lorsque vous êtes en mode push, le contrôleur to view sera celui qui est poussé. Lorsque vous êtes dans une opération de pop, le contrôleur de vue sera celui vers lequel la transition est effectuée et le contrôleur de vue sera celui qui sera sauté.

En outre, le tocontrôleur de vue doit être ajouté en tant que sous-vue de containerViewdans le contexte de transition.

Une fois votre animation 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 ( lecture facultative ), vous voudrez peut-être voir comment j'ai effectué la transition sur laquelle je travaillais. Ce code est un peu plus hacky et je l'ai écrit assez rapidement donc je ne dirais pas que c'est un excellent code d'animation mais il montre toujours comment faire la partie animation.

La mienne était une transition vraiment simple; Je voulais imiter la même animation que UINavigationController, mais au lieu de l'animation `` page suivante en haut '', je voulais implémenter une animation 1: 1 de l'ancien contrôleur de vue en même temps que la nouvelle vue contrôleur apparaît. Cela a pour effet de donner l'impression que les deux contrôleurs de vue sont attachés l'un à l'autre.

Pour l'opération de poussée, cela nécessite d'abord de définir l' toViewControllerorigine de la vue du sur l'écran hors axe x, de l'ajouter comme sous-vue du containerView, de l'animer sur l'écran en la mettant origin.xà zéro. En même temps, j'anime la fromViewControllervue en la origin.xdésactivant 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. Ajoutez le en toViewControllertant que sous-vue du containerView, et animez le fromViewControllervers la droite pendant que vous l'animez toViewControllerdepuis 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 aperçu de l'ensemble du fichier Swift:

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

Alan Zeino
la source
Génial!. Ce que je voulais faire, c'est JUSTE animer en sens inverse. J'ai examiné d'autres solutions, mais toutes présentent des clignotements à gauche et à droite de l'écran. Il semble que l'animation implicite de changement alpha ne puisse pas être supprimée avec eux. Seule cette solution a résolu le problème.
beshio
oui, c'est la seule solution moderne correcte. (Cela ne me dérange pas, mais c'est exactement la même que la solution que j'ai tapée ci-dessous! :))
Fattie
@AlanZeino Que se passe-t-il si à l'intérieur du même ViewController vous avez besoin d'avoir différentes animations pour un clic de bouton différent? Donc, pour le bouton 1, vous avez besoin d'une animation de dissolution, pour le bouton 2, vous avez besoin de la transition par défaut.
jzeferino
7

Il y a UINavigationControllerDelegate et UIViewControllerAnimatedTransitioning là-bas, vous pouvez changer l'animation pour tout ce que vous voulez.

Par exemple, il s'agit d'une 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())
    })
}

}

Puis

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/

eilas
la source
6

Basé sur la réponse mise à jour pour swift 4jordanperry

Pour pousser 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 la 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)
vp2698
la source
5

Voici comment j'ai fait la même chose dans Swift:

Pour Push:

    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 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, ce n'est peut-être pas correct. J'ai remplacé viewWillDisappear:animated:et ajouté le code pop là-dedans:

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

    super.viewWillDisappear(animated)
djbp
la source
5

@Luca Davanzo's answer in Swift 4.2

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: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.popViewController(animated: 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: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
        self.addTransition(transitionType: type, duration: duration)
        self.pushViewController(vc, animated: false)
    }

    private func addTransition(transitionType type: CATransitionType = .fade, duration: CFTimeInterval = 0.3) {
        let transition = CATransition()
        transition.duration = duration
        transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        transition.type = type
        self.view.layer.add(transition, forKey: nil)
    }

}
Serj Rubens
la source
4

J'essayais récemment de faire quelque chose de similaire. J'ai décidé que je n'aimais pas l'animation coulissante de UINavigationController, mais je ne voulais pas non plus faire les animations que UIView vous donne comme curl ou quelque chose comme ça. Je voulais faire un fondu enchaîné entre les vues lorsque je pousse ou que j'éclate.

Le problème là-bas implique le fait que la vue supprime littéralement la vue ou en saute une par-dessus la vue actuelle, donc un fondu ne fonctionne pas. La solution à laquelle j'ai abouti consistait à prendre ma nouvelle vue et à l'ajouter en tant que sous-vue à la vue de dessus actuelle de la pile UIViewController. Je l'ajoute avec un alpha de 0, puis fais un fondu enchaîné. Une fois la séquence d'animation terminée, je pousse la vue sur la pile sans l'animer. Je reviens ensuite à l'ancien topView et nettoie les choses que j'avais modifiées.

C'est un peu plus compliqué que cela, car vous avez les éléments de navigation que vous devez ajuster pour que la transition soit correcte. De plus, si vous effectuez une rotation, vous devez ensuite ajuster les tailles d'image lorsque vous ajoutez les vues en tant que sous-vues afin qu'elles s'affichent correctement à l'écran. Voici une partie du code que j'ai utilisé. J'ai sous-classé le UINavigationController et remplacé 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 de bouton de barre gauche, donc je ne vérifie pas s'il est nul avant de le placer dans le tableau que je passe comme contexte pour le délégué d'animation. Si vous faites cela, vous voudrez peut-être faire cette vérification.

Le problème que j'ai trouvé était que si vous plantiez du tout dans la méthode déléguée, cela ne planterait pas le programme. Cela empêche simplement le délégué de terminer, mais vous ne recevez aucun type d'avertissement.
Donc, comme je faisais mon nettoyage dans cette routine de délégué, cela provoquait un comportement visuel étrange car il ne terminait pas le nettoyage.

Le bouton précédent que je crée appelle une méthode "goBack", et cette méthode appelle simplement 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 ça. Vous aurez besoin d'un code supplémentaire si vous souhaitez implémenter des rotations. Vous devrez définir la taille du cadre de vos vues que vous ajoutez en tant que sous-vues avant de les afficher, sinon vous rencontrerez des problèmes d'orientation est paysage, mais la dernière fois que vous avez vu la vue précédente, c'était portrait. Donc, vous l'ajoutez en tant que sous-vue et l'ajoutez en fondu, mais il apparaît en tant que portrait, puis lorsque nous sautons sans animation, la même vue, mais celle qui est dans la pile, est maintenant paysage. Le tout semble un peu génial. L'implémentation de rotation de chacun est un peu différente, donc je n'ai pas inclus mon code ici.

J'espère que cela aide 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 cela fonctionne vraiment bien pour moi à ce stade.

georryan
la source
Bien qu'admirable, ce n'est honnêtement pas la solution maintenant 7 ans plus tard!
Fattie
Vous avez raison. Cette réponse date de 2011. Cela a fonctionné à l'époque, mais les choses ont beaucoup changé depuis. =)
georryan
4

Vous pouvez maintenant utiliser UIView.transition. Notez cela animated:false. Cela fonctionne avec toute option de transition, pop, push ou remplacement de pile.

if let nav = self.navigationController
{
    UIView.transition(with:nav.view, duration:0.3, options:.transitionCrossDissolve, animations: {
        _ = nav.popViewController(animated:false)
    }, completion:nil)
}
Peter DeWeese
la source
1
@Fattie, cette méthode particulière ne fonctionne qu'avec les animations standard telles que les flips et les boucles répertoriées dans developer.apple.com/documentation/uikit/uiviewanimationoptions
Peter DeWeese
3

En utilisant la réponse d'iJordan comme 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

Importez ensuite simplement le fichier UINavigationController + Animation.h et appelez-le normalement:

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

[self.navigationController popViewControllerWithFlip];
DiscDev
la source
Intelligent. Mais pourquoi ne pas ajouter des méthodes push / pop qui prennent un argument UIViewAnimationTransition plutôt qu'un code dur pour flipFromRight?
Jef
@Jef, ce sont des méthodes pratiques - de cette façon, l'implémenteur n'a pas besoin de se souvenir de la valeur UIViewAnimationTransition à transmettre pour chaque type d'animation spécifique, il appelle simplement la méthode avec le nom "anglais" de ce qu'elle veut accomplir.
DiscDev
@Jef également, votre suggestion est certainement valable - si j'utilisais toujours objective-c et que je devais prendre en charge de nombreux styles de transition (certainement pas recommandé car de nombreux styles de transition différents confondraient les utilisateurs), j'aurais 1 méthode qui prend le type UIViewAnimationTransition, puis plusieurs méthodes pratiques pour faciliter le développement.
DiscDev
3

C'est très simple

self.navigationController?.view.semanticContentAttribute = .forceRightToLeft
user2501116
la source
Bienvenue dans StackOverflow: si vous publiez du code, du XML ou des échantillons de données, veuillez mettre ces lignes en surbrillance dans l'éditeur de texte et cliquer sur le bouton "exemples de code" ({}) dans la barre d'outils de l'éditeur ou en utilisant Ctrl + K sur votre clavier pour formater correctement et la syntaxe le met en valeur!
WhatsThePoint
2

Jetez un œil à ADTransitionController , une alternative au remplacement de 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 des actions push et pop telles que Swipe , Fade , Cube , Carrousel , Zoom , etc.

felginep
la source
2

Bien que toutes les réponses ici soient excellentes et la plupart fonctionnent très bien, il existe une méthode légèrement plus simple qui produit le même effet ...

Pour Push:

  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 la 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];
  }];}
tête dans les codes
la source
Cela se bloquera lors de l'ouverture du contrôleur de vue racine.
Abdullah Umer
1

Je ne connais aucun moyen de modifier publiquement l'animation de transition.

Si le bouton « retour » est pas nécessaire que vous devriez utiliser les contrôleurs de vue modales d'avoir la « poussée de bas » / « flip » / « fade » / (≥3.2) transitions « curl page ».


Du côté privé , la méthode -pushViewController:animated:appelle la 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];

Cependant, vous ne pouvez pas modifier la transition "pop" de cette façon.

kennytm
la source
1

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 une pseudo- "Push" d'un nouveau contrôleur de vue comme vous le souhaitez, et lorsque l'animation est terminée, elle configure le contrôleur de navigation comme si vous aviez utilisé la méthode Push standard. Mon exemple vous permet d'animer un slide-in de gauche ou de 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];
    }];
}
RobP
la source
0

À partir de l'exemple d'application, découvrez 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;
}
johndpope
la source
0

Utilisez simplement:

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];
Sergio Andreotti
la source
0

Le réaliser est une vieille question. Je voudrais toujours poster cette réponse, car j'ai eu quelques problèmes pour en créer plusieurs viewControllersavec les réponses proposées. Ma solution est de sous UINavigationController-classer et de remplacer toutes les méthodes pop et 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
Christian Otkjær
la source
0

Je sais que ce fil est vieux, mais je pensais que je mettrais mes deux cents. Vous n'avez pas besoin de faire une animation personnalisée, il y a une façon simple (peut-être hacky) de le faire. Au lieu d'utiliser push, créez un nouveau contrôleur de navigation, faites du nouveau contrôleur de vue le contrôleur de vue racine de ce contrôleur de navigation, puis présentez le contrôleur de navigation à partir du contrôleur de navigation d'origine. Le cadeau est facilement personnalisable avec de nombreux styles, et pas besoin de faire une animation personnalisée.

Par exemple:

UIViewcontroller viewControllerYouWantToPush = UIViewController()
UINavigationController newNavController = UINavigationController(root: viewControllerYouWantToView)
newNavController.navBarHidden = YES;
self.navigationController.present(newNavController)

Et vous pouvez changer le style de présentation comme vous le souhaitez.

Ethan Zhao
la source
-1

J'ai trouvé un moyen légèrement récursif de le faire qui fonctionne pour mes besoins. J'ai une variable d'instance BOOL que j'utilise pour bloquer l'animation popping normale et remplacer mon propre message pop non animé. La variable est initialement définie sur NO. Lorsque vous appuyez sur le bouton Précédent, la méthode déléguée le définit sur OUI et envoie un nouveau message pop non animé à la barre de navigation, appelant ainsi à nouveau la même méthode déléguée, cette fois avec la variable définie sur OUI. Lorsque la variable est définie sur YES, la méthode déléguée la définit sur NO et renvoie YES pour permettre à la pop non animée de se produire. Après le retour du deuxième appel de délégué, nous nous retrouvons dans le premier, où NO est renvoyé, bloquant la pop animée d'origine! Ce n'est pas aussi désordonné que ça en a l'air. 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;
    }
}

Travaille pour moi.

CharlieMezak
la source