Jak zmienić animacje Push i Pop w aplikacji opartej na nawigacji

Mam aplikację opartą na nawigacji i chcę zmienić animację animacji push i pop. Jak miałbym to zrobić?

Author: zpasternack, 2010-02-07

23 answers

Zrobiłem następujące i działa dobrze.. i jest prosty i łatwy do zrozumienia..

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

I to samo dla push..


Wersja 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)
 251
Author: Magnus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-08 10:53:52

Tak zawsze udawało mi się wykonać to zadanie.

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

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


Nadal otrzymuję wiele opinii od tego, więc zamierzam iść do przodu i zaktualizować go, aby używać bloków animacji, co jest zalecanym przez Apple sposobem na animacje.

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

Dla Pop:

[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
                         }];
[self.navigationController popViewControllerAnimated:NO];
 256
Author: jordanperry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-06 19:01:38

For push

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

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

Dla pop

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

[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];
[self.navigationController popViewControllerAnimated:NO];
 29
Author: Ted,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-23 03:41:28

@Magnus odpowiedz, tylko wtedy dla 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)

Niektóre sidenoty:

Możesz to zrobić również za pomocą Segue, po prostu zaimplementuj to w prepareForSegue lub shouldPerformSegueWithIdentifier. jednakże , spowoduje to zachowanie domyślnej animacji. Aby to naprawić, musisz przejść do storyboardu, kliknij Segue i odznacz pole "Animates". Ale to ograniczy Twoją aplikację dla IOS 9.0 i nowszych (przynajmniej gdy zrobiłem to w Xcode 7).

Podczas wykonywania w segue, dwie ostatnie linie powinny być zastąpione z:

self.navigationController?.popViewControllerAnimated(false)
Nawet jeśli ustawiłem false, to go ignoruje.
 17
Author: CularBytes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-14 11:41:45

Pamiętaj, że w , extension to zdecydowanie twoi przyjaciele!

public extension UINavigationController {

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

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

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

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

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

}
 13
Author: Luca Davanzo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-05 14:14:50

Jak zmienić animacje Push i Pop w aplikacji opartej na nawigacji...

Na rok 2018, "ostateczna odpowiedź!"

Preambuła powiedz, że jesteś nowy w rozwoju iOS, być może z Androida. Bardzo mylące jest to, że Apple zapewnia dwa (2) przejścia, które można łatwo wykorzystać. Są to: "crossfade" i "flip". Teraz dwa najbardziej bezużyteczne przejścia w całym informatyce to "crossfade" i "flip" - których nikt nie używa. Jeśli chcesz zrobić najzwyklejsze przejścia, takie jak "slide off", musisz wykonać ogromną ilość pracy. Ta praca, jest wyjaśniona w tym poście!

  • Po pierwsze, jeśli chcesz użyć jeden z dwóch animów, które zapewnia Apple (crossfade, flipy), jest prosty-użyj rozwiązania @PeterDeWeese powyżej.

  • Po drugie, jest stary szybki i brudny CATransition quickfix. to jest wyjaśnione szczegółowo tutaj. To naprawdę nie działa i nie jest realistyczne rozwiązanie.

W przeciwnym razie, o dziwo, musisz się postarać, aby zrobić pełne przejście niestandardowe .

Do powtórzenia:

nawet jeśli chcesz najprostszego, najczęściej używanego przejścia typu move-over / move-off, na dobre i na złe musisz zaimplementować pełne przejście niestandardowe.

Oto Jak to zrobić ...

1. Potrzebujesz niestandardowego UIViewControllerAnimatedTransitioning

  1. Potrzebujesz własnego boola jak popStyle. (Czy to wskakujesz, czy wyskakujesz?)

  2. Musisz dołączyć transitionDuration (trivial) i główne wywołanie, animateTransition

  3. W rzeczywistości będziesz musiał napisać dwie różne procedury dla inside animateTransition. Jeden za pchnięcie, a drugi za pop. Prawdopodobnie nazwij je animatePush i animatePop. Wewnątrz animateTransition, po prostu rozgałęziaj się popStyle do dwóch procedur

  4. W poniższym przykładzie wykonujemy proste przesunięcie / przesunięcie

  5. W Twojej animatePush i animatePop rutynie. Musisz dostać " od widok " i "do oglądania". (How-to pokazano w przykładzie.)

  6. I najważniejsze, co musisz zrobić, to rzeczywiście, addSubview dla nowego widoku "do".

  7. Musisz zadzwonić completeTransition na koniec anime

Więc ..

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

I wtedy ...

2. Użyj go w kontrolerze widoku.

Zauważ, że ty tylko trzeba to zrobić w kontrolerze widoku" pierwszy".

Ten, który wskoczysz na górę, nie rób nic. Spokojnie. Więc twoja klasa...
class SomeScreen: UIViewController {
}

Staje się...

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
    }
}
To wszystko.

Naciśnij i pop dokładnie tak, jak zwykle, bez zmian. Przeć ...

let n = UIStoryboard(name: "nextScreenStoryboardName", bundle: nil)
          .instantiateViewController(withIdentifier: "nextScreenStoryboardID")
          as! NextScreen
navigationController?.pushViewController(n, animated: true)
Jeśli chcesz, możesz to zrobić na następnym ekranie:]}
class NextScreen: TotallyOrdinaryUIViewController {

    @IBAction func userClickedBackOrDismissOrSomethingLikeThat() {

        navigationController?.popViewController(animated: true)
    }
}

To "takie proste": O

 11
Author: Fattie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-17 22:31:51

Korzystanie z prywatnych połączeń jest złym pomysłem, ponieważ Apple nie zatwierdza już aplikacji, które to robią. Może mógłbyś spróbować tego:

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


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

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

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

//Start Animation
[UIView commitAnimations];
 10
Author: nicktmro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-27 23:03:45

Ponieważ jest to najlepszy wynik w Google, pomyślałem, że podzielę się tym, co uważam za najbardziej rozsądny sposób; czyli użycie interfejsu API przejścia iOS 7+. Zaimplementowałem to dla iOS 10 z Swift 3.

Jest to dość proste, aby połączyć to z tym, jak UINavigationController animuje się między dwoma kontrolerami widoku, jeśli utworzysz podklasę UINavigationController i zwrócisz instancję klasy, która jest zgodna z protokołem UIViewControllerAnimatedTransitioning.

Na przykład tutaj jest moja UINavigationController podklasa:

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

}

Widać, że ustawiłem UINavigationControllerDelegate do siebie, a w rozszerzeniu na mojej podklasie implementuję metodę w UINavigationControllerDelegate, która pozwala zwrócić Niestandardowy kontroler animacji (np. NavigationControllerAnimation). Ten niestandardowy kontroler animacji zastąpi animację magazynową.

Prawdopodobnie zastanawiasz się, dlaczego przekazuję operację do instancji NavigationControllerAnimation poprzez jej inicjalizację. Robię to tak, że w implementacji NavigationControllerAnimation protokołu UIViewControllerAnimatedTransitioning wiem, co to jest operacja (np. ' push 'lub ' pop'). To pomaga wiedzieć, jakiego rodzaju powinienem zrobić animację. W większości przypadków chcesz wykonać inną animację w zależności od operacji.

Reszta jest dość standardowa. Zaimplementuj dwie wymagane funkcje w protokole UIViewControllerAnimatedTransitioning i Animuj w dowolny sposób:
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
        }
    }
}

Należy pamiętać, że dla każdego rodzaju operacji (np. 'push' lub 'pop') Kontrolery widoku to I from będą różne. Gdy jesteś w operacji push, aby wyświetlić kontroler będzie tym, który jest pchany. Gdy jesteś w operacji pop, kontroler widoku to będzie tym, który jest przenoszony, a kontroler widoku od będzie tym, który jest wyrzucany.

Ponadto kontroler widoku to musi być dodany jako podgląd podrzędny containerView w kontekście przejścia.

Po zakończeniu animacji należy wywołać transitionContext.completeTransition(true). Jeśli wykonujesz interaktywne Przejście, będziesz musiał dynamicznie zwracać Bool do completeTransition(didComplete: Bool), w zależności od tego, czy przejście zostało ukończone na koniec animacji.

Wreszcie ( opcjonalne czytanie ), może chcesz zobaczyć, jak zrobiłem przejście, nad którym pracowałem. Ten kod jest trochę bardziej hacky i napisałem go dość szybko, więc nie powiedziałbym, że jest to świetny kod animacji, ale nadal pokazuje, jak zrobić część animacji.

Mój był bardzo prostym przejściem; chciałem naśladować tę samą animację, którą zwykle robi UINavigationController, ale zamiast animacji "Następna strona na górze", chciałem zaimplementuj animację 1:1 starego kontrolera widoku w tym samym czasie, w którym pojawia się nowy kontroler widoku. Powoduje to, że dwa kontrolery widoku wydają się być przypięte do siebie.

Dla operacji push, która wymaga ustawienia początku widoku toViewController na ekranie poza osią x, dodanie go jako podglądu podrzędnego containerView, animowanie go na ekranie przez ustawienie origin.x na zero. W tym samym czasie animuję widok fromViewController, ustawiając jego origin.x poza ekranem:

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

Operacja pop jest w zasadzie odwrotnością. Dodaj toViewController jako podgląd podrzędny containerView i Animuj fromViewController w prawo, gdy animujesz w toViewController od lewej:

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

Oto gist z całym plikiem swift:

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

 8
Author: Alan Zeino,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-08 00:12:47

Istnieją UINavigationControllerDelegate i UIViewControllerAnimatedTransitioning tam możesz zmienić animację na cokolwiek chcesz.

Na przykład jest to pionowa animacja pop dla 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())
    })
}

}

A następnie

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

Przydatny tutorial https://www.objc.io/issues/5-ios7/view-controller-transitions/

 6
Author: eilas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-30 15:05:34

[3]} Ostatnio próbowałem zrobić coś podobnego. Zdecydowałem, że nie podoba mi się animacja przesuwania UINavigationController, ale nie chciałem również robić animacji, które UIView daje jak curl lub coś w tym stylu. Chciałem zrobić przejście między widokami, gdy naciskam lub pop.

Problem polega na tym, że widok dosłownie usuwa widok lub przeskakuje go nad bieżącym, więc zanik nie działa. Rozwiązanie, do którego doszedłem, dotyczyło biorąc mój nowy widok i dodając go jako subview do bieżącego widoku z góry na stosie UIViewController. Dodaję go z alfą 0, a następnie robię crossfade. Po zakończeniu sekwencji animacji, przesuwam widok na stos bez animowania go. Następnie wracam do starego topView i sprzątam rzeczy, które zmieniłem.

To trochę bardziej skomplikowane niż to, ponieważ masz navigationItems trzeba dostosować, aby przejście wyglądać poprawnie. Ponadto, jeśli wykonasz jakiś obrót, następnie musisz dostosować rozmiary ramek, dodając widoki jako podglądy, aby wyświetlały się poprawnie na ekranie. Oto część kodu, którego użyłem. Podklasowałem Kontroler UINavigationController i nadpisałem metody push i 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];

 }

Jak wspomniałem w kodzie, zawsze mam lewy przycisk paska, więc nie sprawdzam, czy jest zerowy przed umieszczeniem go w tablicy, którą przekazuję jako kontekst dla delegata animacji. Jeśli to zrobisz, możesz chcieć wykonać ten czek.

The problemem, który znalazłem, było to, że jeśli w ogóle zawiesisz się w metodzie delegata, nie zawiesi to Programu. To po prostu zatrzymuje delegata przed ukończeniem, ale nie dostajesz żadnego ostrzeżenia.
Więc od kiedy robiłam sprzątanie w tej procedurze delegowania, powodowało to dziwne zachowanie wizualne, ponieważ nie kończyło sprzątania.

Przycisk Wstecz, który tworzę wywołuje metodę "goBack", a ta metoda wywołuje rutynę pop.

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

Również, tutaj jest mój pop rutyna.

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


 }
}
To tyle. Będziesz potrzebował dodatkowego kodu, jeśli chcesz zaimplementować rotacje. Musisz ustawić rozmiar ramki widoków, które dodajesz jako podglądy, zanim je pokażesz, w przeciwnym razie napotkasz problemy orientacja jest pozioma, ale ostatnim razem, gdy widziałeś poprzedni widok, był to portret. Tak więc dodajesz go jako podgląd podrzędny i znikasz, ale wyświetla się jako portret, a następnie gdy pop bez animacji, ten sam widok, ale ten, który jest w stack, teraz jest krajobraz. Wszystko wygląda trochę dziwnie. Każda implementacja rotacji jest trochę inna, więc nie umieściłem tutaj mojego kodu do tego. Mam nadzieję, że niektórym to pomoże. Szukałem wszędzie czegoś takiego i nic nie mogłem znaleźć. Nie sądzę, aby to była idealna odpowiedź, ale w tym momencie działa naprawdę dobrze dla mnie.
 4
Author: georryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-25 18:19:09

Oto jak zrobiłem to samo w Swift:

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

Dla Pop:

Zrobiłem to trochę inaczej niż niektóre z powyższych odpowiedzi - ale ponieważ jestem nowy w szybkim rozwoju, może to nie być w porządku. Nadpisałem viewWillDisappear:animated: i dodałem tam kod pop:

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

    super.viewWillDisappear(animated)
 4
Author: djbp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-20 11:22:46

Na podstawie jordanperry ODPOWIEDŹ zaktualizowana dla swift 4

For push UIViewController

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

Dla Pop

UIView.animate(withDuration: 0.75, animations: {() -> Void in
    UIView.setAnimationCurve(.easeInOut)
    UIView.setAnimationTransition(.flipFromLeft, for: (self.navigationController?.view)!, cache: false)
})
navigationController?.popViewController(animated: false)
 4
Author: vp2698,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-26 09:02:48

Używając odpowiedzi ijordana jako inspiracji, dlaczego po prostu nie utworzyć kategorii na UINavigationController do wykorzystania w całej aplikacji, zamiast kopiować / wklejać ten kod animacji wszędzie?

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

Następnie po prostu zaimportuj UINavigationController + Animation.plik h i wywołaj go normalnie:

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

[self.navigationController popViewControllerWithFlip];
 3
Author: DiscDev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-11 16:16:29

Spójrz na ADTransitionController , spadek zastąpienia UINavigationController niestandardowymi animacjami przejścia (jego API pasuje do API UINavigationController), które stworzyliśmy w Applidium.

Możesz używać różnych predefiniowanych animacji dla push i pop akcji, takich jak Swipe, Fade, Cube, Karuzela, Zoom i tak dalej.

 2
Author: felginep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-17 13:58:06

Podczas gdy wszystkie odpowiedzi tutaj są świetne i większość działa bardzo dobrze, istnieje nieco prostsza metoda, która osiąga ten sam efekt...

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

Dla Pop:

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

  [UIView transitionFromView:self.navigationController.topViewController.view toView:nextView duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
    [self.navigationController popViewControllerAnimated:NO];
  }];}
 2
Author: head in the codes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-01 19:49:57

Możesz teraz użyć UIView.transition. Zauważ, że animated:false. Działa to z każdą opcją przejścia, pop, push lub stack replace.

if let nav = self.navigationController
{
    UIView.transition(with:nav.view, duration:0.3, options:.transitionCrossDissolve, animations: {
        _ = nav.popViewController(animated:false)
    }, completion:nil)
}
 2
Author: Peter DeWeese,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-27 12:18:06

To bardzo proste.]}

self.navigationController?.view.semanticContentAttribute = .forceRightToLeft
 2
Author: user2501116,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-13 08:21:27

Zobacz moją odpowiedź na to pytanie , aby dowiedzieć się, jak to zrobić w znacznie mniejszej liczbie linii kodu. Ta metoda pozwala na animowanie pseudo - "Push" nowego kontrolera widoku w dowolny sposób, a po zakończeniu animacji ustawia Kontroler nawigacyjny tak, jak gdybyś użył standardowej metody Push. Mój przykład pozwala animować slajd z lewej lub z prawej strony. Kod powtórzony tutaj dla wygody:

-(void) showVC:(UIViewController *) nextVC rightToLeft:(BOOL) rightToLeft {
    [self addChildViewController:neighbor];
    CGRect offscreenFrame = self.view.frame;
    if(rightToLeft) {
        offscreenFrame.origin.x = offscreenFrame.size.width * -1.0;
    } else if(direction == MyClimbDirectionRight) {
        offscreenFrame.origin.x = offscreenFrame.size.width;
    }
    [[neighbor view] setFrame:offscreenFrame];
    [self.view addSubview:[neighbor view]];
    [neighbor didMoveToParentViewController:self];
    [UIView animateWithDuration:0.5 animations:^{
        [[neighbor view] setFrame:self.view.frame];
    } completion:^(BOOL finished){
        [neighbor willMoveToParentViewController:nil];
        [neighbor.view removeFromSuperview];
        [neighbor removeFromParentViewController];
        [[self navigationController] pushViewController:neighbor animated:NO];
        NSMutableArray *newStack = [[[self navigationController] viewControllers] mutableCopy];
        [newStack removeObjectAtIndex:1]; //self, just below top
        [[self navigationController] setViewControllers:newStack];
    }];
}
 1
Author: RobP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:25

Nie wiem, w jaki sposób można zmienić animację przejścia publicznie.

Jeśli przycisk "Wstecz" nie jest konieczny, powinieneś użyć Kontrolery widoku modalnego aby przejść" push from bottom " / "flip" / "fade" / (≥3.2)" page curl".


Po stronie private metoda -pushViewController:animated: wywołuje nieudokumentowaną metodę -pushViewController:transition:forceImmediate:, więc np. jeśli chcesz przejść od lewej do prawej, możesz użyć

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

You can ' t change the "pop" Przejście W ten sposób, jednak.

 0
Author: kennytm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:25

Z przykładowej aplikacji, sprawdź tę odmianę. 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
Author: johndpope,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-14 04:37:12

Po prostu użyj:

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
Author: Sergio Andreotti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-23 15:43:18

/ Align = "left" / Nadal chciałbym zamieścić tę odpowiedź, ponieważ miałem kilka problemów z zaproponowanymi odpowiedziami. Moim rozwiązaniem jest podklasowanie UINavigationController i nadpisanie wszystkich metod pop i push.

FlippingNavigationController.h

@interface FlippingNavigationController : UINavigationController

@end

FlippingNavigationController.m:

#import "FlippingNavigationController.h"

#define FLIP_DURATION 0.5

@implementation FlippingNavigationController

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

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

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

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

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

    return viewControllers;
}

@end
 0
Author: Christian Otkjær,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-30 13:31:47

Znalazłem na to łagodnie rekurencyjny sposób, który działa dla moich celów. Mam zmienną instancję BOOL, której używam do blokowania normalnej animacji popping i zastępowania własnej Nie animowanej wiadomości pop. Zmienna jest początkowo ustawiona na NO. Po naciśnięciu przycisku Wstecz metoda delegate ustawia ją na YES i wysyła nową Nie animowaną wiadomość pop do paska nav, wywołując ponownie tę samą metodę delegate, tym razem ze zmienną ustawioną na YES. Ze zmienną ustawioną na YES, metoda delegate ustawia go na NO i zwraca YES, aby umożliwić wystąpienie nieanimowanego pop. Po powrocie drugiego wezwania delegata, kończymy z powrotem w pierwszym, gdzie nie jest zwracane, blokując oryginalny animowany pop! Właściwie nie jest tak brudny, jak się wydaje. Moja metoda shouldPopItem wygląda tak:

- (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;
    }
}
Mi pasuje.
 -1
Author: CharlieMezak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-23 05:37:04