So ändern Sie die Push- und Pop-Animationen in einer navigationsbasierten App


221

Ich habe eine navigationsbasierte Anwendung und möchte die Animation der Push- und Pop-Animationen ändern. Wie würde ich das machen?

Bearbeiten Sie 2018

Es gab viele Antworten auf diese Frage und es ist schon eine ganze Weile her. Ich habe die Antwort auf das, was ich jetzt für die relevanteste halte, neu gewählt. Wenn es jemanden gibt, der anders denkt, lass es mich bitte in den Kommentaren wissen


25
Ab iOS 7 gibt es dafür eine offizielle API; siehe UINavigationControllerDelegate ‚s individuelle Übergangsanimation Unterstützung. Es gibt auch ein WWDC 2013 Video dazu.
Jesse Rusak

Ich habe eine Antwort (unten) hinzugefügt, um dies in Swift zu tun. Ich bin auf diese Frage gestoßen, in der ich nach Swift-Implementierungen gefragt habe, also dachte ich, ich würde mich meiner nachfolgenden Implementierung anschließen.
DJBP

1
Ein sehr gutes Tutorial mit der offiziellen API (iOS 7+) finden Sie unter: bradbambara.wordpress.com/2014/04/11/…
nikolovski

1
@ JesseRusak aktualisierter Link zum WWDC 2013 Video: developer.apple.com/videos/play/wwdc2013-218
Wojciech Rutkowski

1
Ich habe meine akzeptierte Antwort geändert. Hoffe das hilft! GLHF
Jab

Antworten:


35

So ändern Sie die Push- und Pop-Animationen in einer navigationsbasierten App ...

Für 2019 die "endgültige Antwort!"

Präambel:

Angenommen, Sie sind neu in der iOS-Entwicklung. Verwirrenderweise bietet Apple zwei Übergänge, die leicht verwendet werden können. Dies sind: "Crossfade" und "Flip".

Aber natürlich sind "Crossfade" und "Flip" nutzlos. Sie werden nie benutzt. Niemand weiß, warum Apple diese beiden nutzlosen Übergänge bereitgestellt hat!

So:

Angenommen, Sie möchten einen gewöhnlichen, allgemeinen Übergang wie "Folie" ausführen. In diesem Fall müssen Sie eine RIESIGE Menge Arbeit erledigen! .

Diese Arbeit wird in diesem Beitrag erklärt.

Nur um es zu wiederholen:

Überraschenderweise: Wenn Sie mit iOS die einfachsten, häufigsten alltäglichen Übergänge (z. B. eine normale Folie) wünschen , müssen Sie die gesamte Arbeit der Implementierung eines vollständigen benutzerdefinierten Übergangs erledigen .

So geht's ...

1. Sie benötigen eine benutzerdefinierte UIViewControllerAnimatedTransitioning

  1. Du brauchst einen eigenen Bool popStyle. (Knallt es auf oder ab?)

  2. Sie müssen transitionDuration(trivial) und den Hauptaufruf einschließen ,animateTransition

  3. Tatsächlich müssen Sie zwei verschiedene Routinen für inside schreiben animateTransition. Eine für den Push und eine für den Pop. Nennen Sie sie wahrscheinlich animatePushund animatePop. Im Inneren animateTransitionverzweigen Sie einfach popStylezu den beiden Routinen

  4. Das folgende Beispiel führt einen einfachen Um- / Abzug durch

  5. In Ihren animatePushund animatePopRoutinen. Sie müssen das "from view" und das "to view" erhalten. (Wie das geht, erfahren Sie im Codebeispiel.)

  6. und Sie müssen addSubview für die neue "to" -Ansicht.

  7. und du musstcompleteTransition am Ende deines Anime anrufen

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

Und dann ...

2. Verwenden Sie es in Ihrem View Controller.

Hinweis: Seltsamerweise müssen Sie dies nur im "ersten" Ansichts-Controller tun . (Der, der "darunter" ist.)

Mit dem, den Sie oben platzieren , tun Sie nichts . Einfach.

Also deine Klasse ...

class SomeScreen: UIViewController {
}

wird...

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

Das ist es.

Push and Pop genau wie gewohnt, keine Änderung. Drücken ...

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

und um es zu öffnen, können Sie dies auf dem nächsten Bildschirm tun, wenn Sie möchten:

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

Puh.


3. Genießen Sie auch die anderen Antworten auf dieser Seite, in denen erläutert wird, wie AnimatedTransitioning überschrieben wird

Scrollen Sie zu @AlanZeino und antworten Sie auf @elias, um weitere Informationen darüber zu erhalten, wie Sie heutzutage AnimatedTransitioningin iOS-Apps vorgehen können !


Ausgezeichnet! Wenn ich möchte, dass die Navigationswisch-Geste das gleiche AnimatedTransitioning unterstützt. Irgendeine Idee?
Sam Chi Wen

danke @samchiwen - in der Tat ist das genau was animatePushund animatePopsind .. die zwei verschiedenen Richtungen!
Fattie

268

Ich habe folgendes getan und es funktioniert gut .. und ist einfach und leicht zu verstehen ..

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

Und das gleiche für Push ..


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)

34
+1, das ist wirklich die vernünftigste Lösung. Nur eine kleine Anmerkung für zukünftige Besucher: Der Animated:NOTeil ist wichtig. Wenn YESbestanden, mischen sich die Animationen und verursachen lustige Effekte.
DarkDust

12
Die bisher beste Lösung. Und für Anfänger vergessen Sie nicht, QuartCore (#import <QuartzCore / QuartzCore.h>)
einzuschließen

4
Das einzige Problem, das ich mit dieser Lösung habe, ist, dass viewDidAppear des Push-Viewcontrollers sofort aufgerufen wird, nachdem ich es ohne Animation gepusht habe. Gibt es einen Weg, um das zu umgehen?
Pedro Mancheno

9
Mein Problem mit diesem Code ist, dass jede Ansicht beim Ein- oder Ausschieben grau oder weiß zu blinken scheint.
Chris

1
überprüft auf iOS 7.1.2 und iOS 8.3 - dieser Code funktioniert gut auch funktioniert für MethodesetViewControllers:
proff

256

So habe ich es immer geschafft, diese Aufgabe zu erledigen.

Für 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];

Für 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];


Ich bekomme immer noch viel Feedback, daher werde ich es aktualisieren, um Animationsblöcke zu verwenden. Dies ist die von Apple empfohlene Methode, um Animationen zu erstellen.

Für 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];
                         }];

Für Pop:

[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
                         }];
[self.navigationController popViewControllerAnimated:NO];

3
Danke dafür. Der Pop wird jedoch automatisch vom UINavigationController ausgeführt. Wie überschreiben Sie dieses Verhalten, damit Sie Ihre benutzerdefinierte Pop-Logik aufrufen können?
Joshua Frank

1
@stuckj eigentlich funktioniert es !!! Sie müssen nur ersetzen superdurchself.navigationController
holierthanthou84

Gibt es eine Möglichkeit, eine Folie von links anstelle der Standardfolie von rechts zu erhalten?
Shim

Die erste zeigt die neue Ansicht überhaupt nicht. Die zweite zeigt keine Animation. Sehr schlechte Antwort! iOS 7.
Dmitry

2
Warum haben Sie angegeben, UIViewControllereinen Namen ohne "ViewController" -Teil zu unterklassifizieren? Dieser Name ist besser für UIView geeignet.
user2159978

29

zum schieben

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

für 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];

19

@ Magnus Antwort, nur dann für 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)

Einige Nebenbemerkungen:

Sie können dies auch mit Segue tun. Implementieren Sie dies einfach in prepareForSegueoder shouldPerformSegueWithIdentifier. Dadurch bleibt jedoch auch die Standardanimation erhalten. Um dies zu beheben, müssen Sie zum Storyboard gehen, auf Segue klicken und das Kontrollkästchen 'Animates' deaktivieren. Dies wird jedoch Ihre App für IOS 9.0 und höher einschränken (zumindest als ich es in Xcode 7 getan habe).

Wenn Sie in einem Segue arbeiten, sollten die letzten beiden Zeilen ersetzt werden durch:

self.navigationController?.popViewControllerAnimated(false)

Obwohl ich falsch gesetzt habe, ignoriert es es irgendwie.


So entfernen Sie die schwarze Farbe im Hintergrund am Ende der Animation.
Madhu

Funktioniert nicht für Push View Controller Animation funktioniert für Pop View Controller
Mukul More

16

Denken Sie daran , dass in Swift , Erweiterung sind auf jeden Fall Ihre Freunde!

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

}

11

Die Verwendung privater Anrufe ist eine schlechte Idee, da Apple Apps, die dies tun, nicht mehr genehmigt. Vielleicht könnten Sie das versuchen:

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

Es funktioniert nur "halb" - es wird das schwierigere Problem der Pop-Animationen nicht lösen.
Adam

Ich mag diese Lösung besser und ja, es funktioniert. Wenn Sie private Methoden verwenden, werden Sie mit Sicherheit abgelehnt.
Benjamin Intal

@nicktmro das ist der private API-Aufruf. Ich habe keine bemerkt.
Franklin

@Franklin Vor einiger Zeit gab es hier eine Diskussion über -pushViewController:transition:forceImmediate:die Verwendung, was eine schlechte Idee wäre.
Nicktmro

9

Da dies das Top-Ergebnis bei Google ist, dachte ich, ich würde mitteilen, was meiner Meinung nach der vernünftigste Weg ist. Dies ist die Verwendung der iOS 7+ -Übergangs-API. Ich habe dies für iOS 10 mit Swift 3 implementiert.

Es ist ziemlich einfach, dies mit der UINavigationControllerAnimation zwischen zwei Ansichts-Controllern zu kombinieren, wenn Sie eine Unterklasse UINavigationControllereiner Klasse erstellen und eine Instanz einer Klasse zurückgeben, die dem UIViewControllerAnimatedTransitioningProtokoll entspricht.

Zum Beispiel ist hier meine UINavigationControllerUnterklasse:

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

}

Sie können sehen, dass ich das UINavigationControllerDelegateauf sich selbst setze , und in einer Erweiterung in meiner Unterklasse implementiere ich die Methode UINavigationControllerDelegate, mit der Sie einen benutzerdefinierten Animationscontroller (dh NavigationControllerAnimation) zurückgeben können. Dieser benutzerdefinierte Animationscontroller ersetzt die Standardanimation für Sie.

Sie fragen sich wahrscheinlich, warum ich die Operation NavigationControllerAnimationüber ihren Initialisierer an die Instanz übergebe. Ich mache das so, dass ich bei NavigationControllerAnimationder Implementierung des UIViewControllerAnimatedTransitioningProtokolls weiß, was die Operation ist (dh 'Push' oder 'Pop'). Dies hilft zu wissen, welche Art von Animation ich machen soll. Meistens möchten Sie je nach Vorgang eine andere Animation ausführen.

Der Rest ist ziemlich normal. Implementieren Sie die beiden erforderlichen Funktionen im UIViewControllerAnimatedTransitioningProtokoll und animieren Sie, wie Sie möchten:

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

Es ist wichtig zu bedenken, dass für jede Art von Operation (dh 'Push' oder 'Pop') die Zu- und Ab-Controller unterschiedlich sind. Wenn Sie sich in einer Push-Operation befinden, wird der Controller für die Anzeige angezeigt. Wenn Sie sich in einem Pop-Vorgang befinden, wird der Controller für die Ansicht angezeigt, auf den umgestellt wird, und der Controller für die Ansicht wird angezeigt.

Außerdem muss der toAnsichts-Controller als Unteransicht des containerViewim Übergangskontext hinzugefügt werden .

Wenn Ihre Animation abgeschlossen ist, müssen Sie anrufen transitionContext.completeTransition(true). Wenn Sie einen interaktiven Übergang tun, müssen Sie dynamisch eine Rückkehr Boolzu completeTransition(didComplete: Bool), je nachdem , ob der Übergang am Ende der Animation abgeschlossen ist.

Schließlich ( optionales Lesen ) möchten Sie vielleicht sehen, wie ich den Übergang gemacht habe, an dem ich gearbeitet habe. Dieser Code ist etwas hackiger und ich habe ihn ziemlich schnell geschrieben, daher würde ich nicht sagen, dass er großartiger Animationscode ist, aber er zeigt immer noch, wie man den Animationsteil macht.

Meins war ein wirklich einfacher Übergang; Ich wollte dieselbe Animation nachahmen, die UINavigationController normalerweise ausführt, aber anstelle der Animation "Nächste Seite über dem oberen Rand" wollte ich gleichzeitig mit der neuen Ansicht eine 1: 1-Animation des alten Ansichts-Controllers implementieren Controller erscheint. Dies hat den Effekt, dass die beiden Ansichts-Controller so aussehen, als wären sie miteinander verbunden.

Für den Push-Vorgang muss zuerst der toViewControllerAnsichtsursprung der Ansicht auf der x-Achse außerhalb des Bildschirms festgelegt, als Unteransicht der hinzugefügt containerViewund auf dem Bildschirm animiert werden , indem dieser Wert origin.xauf Null gesetzt wird. Gleichzeitig animiere ich die fromViewControllerAnsicht origin.xdes Bildschirms, indem ich sie vom Bildschirm aus deaktiviere:

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

Die Pop-Operation ist im Grunde die Umkehrung. Fügen Sie das toViewControllerals Unteransicht des hinzu containerViewund animieren fromViewControllerSie das rechts weg , während Sie das toViewControllervon links animieren :

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

Hier ist ein Kern der gesamten schnellen Datei:

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


Toll!. Was ich tun wollte, ist NUR in die entgegengesetzte Richtung zu animieren. Ich habe einige andere Lösungen untersucht, aber alle zeigen ein Blinken auf dem linken und rechten Bildschirm. Es sieht so aus, als ob implizite Alpha-Änderungsanimationen mit ihnen nicht entfernt werden können. Nur diese Lösung hat das Problem behoben.
Beshio

Ja, dies ist die einzig richtige moderne Lösung. (Es macht mir nichts aus, aber es ist genau das gleiche wie die Lösung, die ich unten geschrieben habe! :))
Fattie

@AlanZeino Was ist, wenn Sie im selben ViewController verschiedene Animationen für unterschiedliche Schaltflächen benötigen? Für button1 benötigen Sie eine Auflösungsanimation, für button2 benötigen Sie den Standardübergang.
Jzeferino

7

Es gibt UINavigationControllerDelegate und UIViewControllerAnimatedTransitioning. Dort können Sie die Animation für alles ändern, was Sie wollen.

Dies ist beispielsweise eine vertikale Pop-Animation für 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())
    })
}

}}

Und dann

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

Nützliches Tutorial https://www.objc.io/issues/5-ios7/view-controller-transitions/


6

Basierend auf der für schnell aktualisierten Antwort 4jordanperry

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

Für 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)

5

So habe ich es in Swift gemacht:

Für 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)
    })

Für Pop:

Ich habe dies tatsächlich ein wenig anders gemacht als einige der obigen Antworten - aber da ich neu in der Swift-Entwicklung bin, ist es möglicherweise nicht richtig. Ich habe viewWillDisappear:animated:den Pop-Code dort überschrieben und hinzugefügt:

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

    super.viewWillDisappear(animated)

5

@ Luca Davanzos Antwort 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)
    }

}

4

Ich habe kürzlich versucht, etwas Ähnliches zu tun. Ich entschied, dass mir die gleitende Animation des UINavigationControllers nicht gefallen hat, aber ich wollte auch nicht die Animationen machen, die UIView Ihnen wie Curl oder ähnliches gibt. Ich wollte eine Überblendung zwischen den Ansichten machen, wenn ich drücke oder knalle.

Das Problem besteht darin, dass die Ansicht die Ansicht buchstäblich entfernt oder über die aktuelle Ansicht legt, sodass eine Überblendung nicht funktioniert. Die Lösung bestand darin, meine neue Ansicht als Unteransicht zur aktuellen Draufsicht auf dem Stapel des UIViewControllers hinzuzufügen. Ich füge es mit einem Alpha von 0 hinzu und mache dann eine Überblendung. Wenn die Animationssequenz beendet ist, schiebe ich die Ansicht auf den Stapel, ohne sie zu animieren. Ich gehe dann zurück zum alten topView und räume Sachen auf, die ich geändert habe.

Es ist etwas komplizierter, da Sie die Navigationselemente haben, die Sie anpassen müssen, damit der Übergang korrekt aussieht. Wenn Sie eine Drehung durchführen, müssen Sie die Rahmengrößen anpassen, während Sie die Ansichten als Unteransichten hinzufügen, damit sie korrekt auf dem Bildschirm angezeigt werden. Hier ist ein Teil des Codes, den ich verwendet habe. Ich habe den UINavigationController unterklassifiziert und die Push- und Pop-Methoden überschrieben.

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

 }

Wie ich im Code erwähne, habe ich immer ein Element in der linken Leiste, daher überprüfe ich nicht, ob es Null ist, bevor ich es in das Array einfüge, das ich als Kontext für den Animationsdelegierten übergebe. Wenn Sie dies tun, möchten Sie möglicherweise diese Überprüfung durchführen.

Das Problem, das ich gefunden habe, war, dass das Programm nicht abstürzt, wenn Sie in der Delegate-Methode überhaupt abstürzen. Es verhindert nur, dass der Delegat fertig ist, aber Sie erhalten keinerlei Warnung.
Da ich meine Bereinigung in dieser Delegatenroutine durchführte, verursachte dies ein seltsames visuelles Verhalten, da die Bereinigung nicht abgeschlossen wurde.

Die von mir erstellte Zurück-Schaltfläche ruft eine "goBack" -Methode auf, und diese Methode ruft nur die Pop-Routine auf.

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

Auch hier ist meine Pop-Routine.

-(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];
    }


 }
}

Das wars so ziemlich. Sie benötigen zusätzlichen Code, wenn Sie Rotationen implementieren möchten. Sie müssen die Rahmengröße Ihrer Ansichten festlegen, die Sie als Unteransichten hinzufügen, bevor Sie sie anzeigen. Andernfalls treten Probleme auf. Die Ausrichtung ist Querformat, aber das letzte Mal, als Sie die vorherige Ansicht gesehen haben, war es Hochformat. Dann fügen Sie es als Unteransicht hinzu und blenden es ein, aber es wird als Hochformat angezeigt. Wenn wir dann ohne Animation einblenden, ist dieselbe Ansicht, aber die im Stapel befindliche, jetzt Querformat. Das Ganze sieht ein bisschen funky aus. Die Implementierung von Rotation bei jedem ist etwas anders, daher habe ich meinen Code dafür hier nicht angegeben.

Hoffe es hilft einigen Leuten. Ich habe überall nach so etwas gesucht und konnte nichts finden. Ich denke nicht, dass dies die perfekte Antwort ist, aber es funktioniert zu diesem Zeitpunkt wirklich gut für mich.


Obwohl bewundernswert, ist dies jetzt, 7 Jahre später, ehrlich gesagt nicht die Lösung!
Fattie

Du hast recht. Diese Antwort stammt aus dem Jahr 2011. Es hat damals funktioniert, aber seitdem haben sich die Dinge sehr verändert. =)
Georryan

4

Sie können jetzt verwenden UIView.transition. Beachten Sie das animated:false. Dies funktioniert mit jeder Übergangsoption, Pop, Push oder Stack-Ersetzung.

if let nav = self.navigationController
{
    UIView.transition(with:nav.view, duration:0.3, options:.transitionCrossDissolve, animations: {
        _ = nav.popViewController(animated:false)
    }, completion:nil)
}

1
@ Fattie, diese spezielle Methode funktioniert nur mit den Standardanimationen wie Flips und Curls, die unter developer.apple.com/documentation/uikit/uiviewanimationoptions
Peter DeWeese am

3

Erstellen Sie anhand der Antwort von iJordan einfach eine Kategorie auf UINavigationController, die Sie in Ihrer gesamten App verwenden können, anstatt diesen Animationscode überall zu kopieren / einzufügen.

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

Importieren Sie dann einfach die Datei UINavigationController + Animation.h und rufen Sie sie normal auf:

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

[self.navigationController popViewControllerWithFlip];

Klug. Aber warum nicht Push / Pop-Methoden hinzufügen, die ein Argument von UIViewAnimationTransition anstelle von Hardcode für flipFromRight verwenden?
Jef

@Jef Dies sind Convenience-Methoden. Auf diese Weise muss sich der Implementierer nicht merken, welchen UIViewAnimationTransition-Wert für jeden bestimmten Animationstyp übergeben werden soll. Er ruft die Methode nur mit dem "englischen" Namen auf, den er erreichen möchte.
DiscDev

@Jef auch, Ihr Vorschlag ist definitiv gültig - wenn ich noch Objective-C verwenden würde und viele Übergangsstile unterstützen müsste (definitiv nicht empfohlen, da viele verschiedene Übergangsstile Benutzer verwirren würden), hätte ich 1 Methode, die den Typ UIViewAnimationTransition verwendet verschiedene praktische Methoden, um die Entwicklung zu vereinfachen.
DiscDev

3

Es ist sehr einfach

self.navigationController?.view.semanticContentAttribute = .forceRightToLeft

Willkommen bei StackOverflow: Wenn Sie Code-, XML- oder Datenbeispiele veröffentlichen, markieren Sie diese Zeilen im Texteditor und klicken Sie auf die Schaltfläche "Codebeispiele" ({}) in der Editor-Symbolleiste oder verwenden Sie Strg + K auf Ihrer Tastatur, um das Format zu optimieren und Syntax heben es hervor!
WhatsThePoint

2

Schauen Sie sich ADTransitionController an , einen Ersatz für UINavigationController mit benutzerdefinierten Übergangsanimationen (seine API entspricht der API von UINavigationController), die wir bei Applidium erstellt haben.

Sie können verschiedene vordefinierte Animationen für Push- und Pop- Aktionen wie Wischen , Überblenden , Würfeln , Karussell , Zoomen usw. verwenden.


2

Während alle Antworten hier großartig sind und die meisten sehr gut funktionieren, gibt es eine etwas einfachere Methode, die den gleichen Effekt erzielt ...

Für 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];
  }];

Für 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];
  }];}

Dies stürzt ab, wenn Sie zum Root View Controller wechseln.
Abdullah Umer

1

Mir ist nicht bekannt, wie Sie die Übergangsanimation öffentlich ändern können.

Wenn die Schaltfläche „Zurück“ nicht notwendig ist , Sie sollten verwenden modal View - Controller die „Push von unten“ haben / „Flip“ / „faden“ / (≥3.2) „Einrollen“ Übergänge.


Auf der privaten Seite -pushViewController:animated:ruft die Methode die undokumentierte Methode auf -pushViewController:transition:forceImmediate:. Wenn Sie also beispielsweise einen Übergang von links nach rechts wünschen, können Sie diese verwenden

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

Sie können den "Pop" -Übergang jedoch nicht auf diese Weise ändern.


1

In meiner Antwort auf diese Frage finden Sie eine Möglichkeit, dies in weitaus weniger Codezeilen zu tun. Mit dieser Methode können Sie einen Pseudo- "Push" eines neuen Ansichts-Controllers nach Belieben animieren. Wenn die Animation abgeschlossen ist, wird der Navigations-Controller so eingerichtet, als hätten Sie die Standard-Push-Methode verwendet. In meinem Beispiel können Sie entweder ein Slide-In von links oder von rechts animieren. Der Code wird hier zur Vereinfachung wiederholt:

-(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];
    }];
}

0

Schauen Sie sich diese Variante in der Beispiel-App an. 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

Benutz einfach:

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

Dies zu erkennen ist eine alte Frage. Ich möchte diese Antwort immer noch posten, da ich einige Probleme hatte, einige viewControllersmit den vorgeschlagenen Antworten zu knallen . Meine Lösung besteht darin, UINavigationControlleralle Pop- und Push-Methoden zu unterklassifizieren und zu überschreiben.

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

Ich weiß, dass dieser Thread alt ist, aber ich dachte, ich würde meine zwei Cent einbringen. Sie müssen keine benutzerdefinierte Animation erstellen, es gibt eine einfache (möglicherweise hackige) Möglichkeit, dies zu tun. Erstellen Sie anstelle von Push einen neuen Navigationscontroller, machen Sie den neuen Ansichtscontroller zum Stammansichtscontroller dieses Navigationscontrollers und präsentieren Sie dann den Navigationscontroller vom ursprünglichen Navigationscontroller. Present ist mit vielen Stilen leicht anpassbar und es ist nicht erforderlich, eine benutzerdefinierte Animation zu erstellen.

Beispielsweise:

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

Sie können den Präsentationsstil beliebig ändern.


-1

Ich habe einen leicht rekursiven Weg gefunden, der für meine Zwecke funktioniert. Ich habe eine Instanzvariable BOOL, mit der ich die normale Popping-Animation blockiere und meine eigene nicht animierte Pop-Nachricht ersetze. Die Variable wird anfänglich auf NO gesetzt. Wenn Sie auf die Schaltfläche "Zurück" tippen, setzt die Delegatmethode sie auf "JA" und sendet eine neue nicht animierte Popup-Nachricht an die Navigationsleiste. Dadurch wird dieselbe Delegatmethode erneut aufgerufen, diesmal mit der Variablen "JA". Wenn die Variable auf YES gesetzt ist, setzt die Delegate-Methode sie auf NO und gibt YES zurück, damit der nicht animierte Pop auftreten kann. Nachdem der zweite Delegatenanruf zurückgekehrt ist, kehren wir zum ersten zurück, wo NO zurückgegeben wird, wodurch der ursprüngliche animierte Pop blockiert wird! Es ist eigentlich nicht so chaotisch, wie es sich anhört. Meine shouldPopItem-Methode sieht folgendermaßen aus:

- (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;
    }
}

Funktioniert bei mir.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.