Как изменить анимацию Push и Pop в приложении на основе навигации - PullRequest
212 голосов
/ 07 февраля 2010

У меня есть приложение на основе навигации, и я хочу изменить анимацию анимации push и pop. Как бы я это сделал?

Изменить 2018

Было много ответов на этот вопрос, и прошло уже довольно много времени, я переизбрал ответ на то, что я считаю наиболее актуальным сейчас. Если есть кто-то, кто думает иначе, пожалуйста, дайте мне знать в комментариях

Ответы [ 25 ]

263 голосов
/ 14 апреля 2011

Я сделал следующее, и он отлично работает .. и прост и понятен ..

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

И то же самое для толчка ..


Версия 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)
256 голосов
/ 05 мая 2011

Так мне всегда удавалось выполнить эту задачу.

Для толчка:

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

Для поп:

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


Я по-прежнему получаю много отзывов об этом, поэтому я собираюсь обновить его, чтобы использовать блоки анимации, что в любом случае является рекомендованным Apple способом создания анимации.

Для толчка:

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

Для поп:

[UIView animateWithDuration:0.75
                         animations:^{
                             [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                             [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
                         }];
[self.navigationController popViewControllerAnimated:NO];
29 голосов
/ 23 июня 2014

для толчка

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

для поп

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];
23 голосов
/ 03 января 2018

Как изменить анимацию Push и Pop в навигационном приложении ...

Для 2019 года «окончательный ответ!»

Преамбула:

Допустим, вы новичок в разработке для iOS. Смущает, что Apple предоставляет два перехода, которые можно легко использовать. Это: «кроссфейд» и «флип».

Но, конечно, "кроссфейд" и "флип" бесполезны. Они никогда не используются. Никто не знает, почему Apple предоставила эти два бесполезных перехода!

Итак:

Скажем, вы хотите сделать обычный, обычный переход , такой как «слайд». В этом случае, вам придется выполнить ОГРОМНОЕ количество работы! .

Эта работа объясняется в этом посте.

Просто повторить:

Удивительно: с iOS, если вам нужны самые простые, самые обычные, повседневные переходы (например, обычный слайд), вам нужно вся работа по реализации полный пользовательский переход .

Вот как это сделать ...

1. Вам нужен кастом UIViewControllerAnimatedTransitioning

  1. Вам нужен собственный bool вроде popStyle. (Это включается или выключается?)

  2. Вы должны включить transitionDuration (тривиально) и основной вызов, animateTransition

  3. На самом деле вы должны написать две разные подпрограммы для внутреннего animateTransition. Один для толчка, а другой для поп-музыки. Вероятно, назовите их animatePush и animatePop. Внутри animateTransition, просто перейдите на popStyle к двум подпрограммам

  4. В приведенном ниже примере выполняется простое перемещение / удаление

  5. В ваших animatePush и animatePop процедурах. Вы должны получить «из поля зрения» и «для просмотра». (Как это сделать, показано в примере кода.)

  6. и вы должны addSubview для нового просмотра "to".

  7. и вы должны позвонить completeTransition в конце вашего аниме

Итак ..

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

А потом ...

2. Используйте его в вашем контроллере вида.

Примечание: как ни странно, вам нужно только сделать это в «первом» контроллере вида. (Тот, который "под".)

С тем, на который вы попали top , ничего не делайте . Легко.

Так твой класс ...

class SomeScreen: UIViewController {
}

становится ...

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

Вот и все.

Толчок и выдвижение точно так же, как обычно, без изменений. Толкать ...

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

и выскочить, вы можете, если хотите, просто сделать это на следующем экране:

class NextScreen: TotallyOrdinaryUIViewController {

    @IBAction func userClickedBackOrDismissOrSomethingLikeThat() {

        navigationController?.popViewController(animated: true)
    }
}

Уф.


3. Также воспользуйтесь другими ответами на этой странице, которые объясняют, как переопределить AnimatedTransitioning

Перейдите к @AlanZeino и ответу @elias для получения дополнительной информации о том, как AnimatedTransitioning в приложениях iOS в эти дни!

18 голосов
/ 14 июля 2015

@ Магнус ответ, только тогда для 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)

Некоторые sidenotes:

Вы можете сделать это также с Segue, просто внедрите это в prepareForSegue или shouldPerformSegueWithIdentifier. Однако , это также сохранит анимацию по умолчанию. Чтобы это исправить, вам нужно перейти на раскадровку, щелкнуть «Segue» и снять флажок «Animates». Но это ограничит ваше приложение для IOS 9.0 и выше (по крайней мере, когда я сделал это в Xcode 7).

При выполнении перехода две последние строки следует заменить на:

self.navigationController?.popViewControllerAnimated(false)

Даже если я установил значение false, это как бы игнорирует его.

16 голосов
/ 05 апреля 2016

Помните, что в Swift , extension определенно ваши друзья!

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

}
10 голосов
/ 28 марта 2010

Использование личных вызовов - плохая идея, так как Apple больше не одобряет приложения, которые делают это. Может быть, вы могли бы попробовать это:

//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];
9 голосов
/ 08 января 2017

Так как это лучший результат в Google, я подумал, что поделюсь тем, что я считаю самым разумным способом; который должен использовать API перехода iOS 7+. Я реализовал это для iOS 10 с Swift 3.

Довольно просто объединить это с тем, как UINavigationController анимирует между двумя контроллерами представления, если вы создаете подкласс UINavigationController и возвращаете экземпляр класса, который соответствует протоколу UIViewControllerAnimatedTransitioning.

Например, вот мой UINavigationController подкласс:

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

}

Вы можете видеть, что я установил UINavigationControllerDelegate для себя, и в расширении моего подкласса я реализую метод в UINavigationControllerDelegate, который позволяет вам возвращать пользовательский контроллер анимации (т.е. NavigationControllerAnimation). Этот пользовательский контроллер анимации заменит вам стандартную анимацию.

Возможно, вам интересно, почему я передаю эту операцию экземпляру NavigationControllerAnimation через его инициализатор. Я делаю это так, чтобы в реализации NavigationControllerAnimation протокола UIViewControllerAnimatedTransitioning я знал, что это за операция (то есть «push» или «pop»). Это помогает узнать, какую анимацию я должен делать. В большинстве случаев вы хотите выполнить другую анимацию в зависимости от операции.

Остальное довольно стандартно. Реализуйте две обязательные функции в протоколе UIViewControllerAnimatedTransitioning и анимируйте, как вам нравится:

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

Важно помнить, что для каждого отдельного типа операции (т. Е. «Push» или «pop») контроллеры представления «в» и «из» будут разными. Когда вы работаете в режиме push, контроллером для просмотра будет тот, который выдвигается. Когда вы работаете в режиме pop, контроллер для просмотра будет тем, к которому осуществляется переход, и контроллером from для просмотра будет тот, который выдается.

Кроме того, контроллер вида to должен быть добавлен как подпредставление containerView в контексте перехода.

Когда ваша анимация завершится, вы должны позвонить transitionContext.completeTransition(true). Если вы делаете интерактивный переход, вам придется динамически возвращать Bool к completeTransition(didComplete: Bool), в зависимости от того, завершен ли переход в конце анимации.

Наконец ( необязательное чтение ), вы можете посмотреть, как я выполнял переход, над которым я работал. Этот код немного более хакерский, и я написал его довольно быстро, поэтому я бы не сказал, что это отличный анимационный код, но он все же показывает, как выполнять анимационную часть.

Мой был действительно простой переход; Я хотел имитировать ту же анимацию, что обычно делает UINavigationController, но вместо анимации «следующая страница поверх», которую я это делал, я хотел реализовать анимацию 1: 1 старого контроллера представления в то же время, что и новое представление контроллер появляется. Это приводит к тому, что два контроллера представления выглядят так, как будто они прикреплены друг к другу.

Для операции выталкивания, которая сначала требует установки начала координат вида toViewController на экране выключения оси x, добавление его в качестве подпредставления containerView, анимация его на экране, установив для origin.x значение нуль. В то же время я анимирую вид fromViewController, установив origin.x на экране:

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

Поп-операция в основном обратная. Добавьте toViewController как подпредставление containerView и анимируйте fromViewController вправо, как вы анимируете в toViewController слева:

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

Вот суть всего файла swift:

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

7 голосов
/ 30 декабря 2015

Есть UINavigationControllerDelegate и UIViewControllerAnimatedTransitioning, где вы можете изменить анимацию на что угодно.

Например, это вертикальная анимация для 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())
    })
}

}

А потом

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

Полезное руководство https://www.objc.io/issues/5-ios7/view-controller-transitions/

5 голосов
/ 20 января 2015

Вот как я сделал то же самое в Swift:

Для 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)
    })

Для поп:

На самом деле я сделал это немного иначе, чем некоторые из приведенных выше ответов, но, поскольку я новичок в разработке Swift, это может быть неправильно. Я переопределил viewWillDisappear:animated: и добавил туда поп-код:

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

    super.viewWillDisappear(animated)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...