Как изменить анимацию Push и Pop в приложении на основе навигации
У меня есть приложение на основе навигации, и я хочу изменить анимацию push и pop анимации. Как бы я это сделал?
23 ответа:
Я сделал следующее, И это работает прекрасно.. и это просто и легко понять..
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)
это, как мне всегда удавалось выполнить эту задачу.
Для Нажмите:
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];
для 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];
поп
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];
@Magnus ответ, только тогда для 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)
какой-то заметками на полях истории болезни:
вы можете сделать это также с Segue, просто реализовать это в
prepareForSegue
илиshouldPerformSegueWithIdentifier
. , это будет держать анимацию по умолчанию в нем, а также. Чтобы исправить это, вы должны перейти к раскадровке, нажмите кнопку Segue и снимите флажок "анимирует". Но это ограничит ваше приложение для iOS 9.0 и выше (по крайней мере, когда я сделал это в Xcode 7).когда делаешь поворот, то последние две строки следует заменить на:
self.navigationController?.popViewControllerAnimated(false)
хотя я установил false, он как бы игнорирует его.
помните, что в Свифт,расширение определенно ваших друзей!
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) } }
как изменить анимацию Push и Pop в приложении на основе навигации...
на 2018 год, "окончательный ответ!"
преамбуласкажем, вы новичок в разработке iOS, возможно, с Android. Очень смущает, что Apple предоставляет два (2) перехода, которые можно легко использовать. Это: "кроссфейд" и "флип". Теперь два самых бесполезных перехода во всех вычислениях - это "кроссфейд" и "флип", которые никто не использует. Если вы хотите делать самые обычные переходы, например "соскальзывать", вам придется проделать огромный объем работы. Эта работа, объясняется в этом посте!
во-первых, если вы хотите использовать один из двух анимов, которые предоставляет Apple (crossfade, flips), это просто - используйте решение @PeterDeWeese выше.
во-вторых, есть старый быстрый-и-грязный CATransition quickfix. это объясняется подробно здесь. Это действительно не работает, и это не реалистичное решение.
в противном случае, удивительно, вы должны приложить усилия, чтобы сделать полный пользовательский переход.
повторю:
даже если вы просто хотите самый простой, наиболее распространенный переход перемещения/перемещения, к лучшему или худшему вам придется реализовать полный пользовательский переход.
вот как это сделать оно...
1. Вам нужен пользовательский
UIViewControllerAnimatedTransitioning
вам нужен свой собственный bool, как
popStyle
. (Это выскакивает или выскакивает?)необходимо указать
transitionDuration
(тривиальный) и основной вызов,animateTransition
на самом деле вам нужно будет написать две разные процедуры для inside
animateTransition
. Один для толчка и для поп. Наверное, назовите ихanimatePush
иanimatePop
. ВнутриanimateTransition
, просто ответвление наpopStyle
к двум подпрограммамв приведенном ниже примере выполняется простое перемещение / перемещение
в своем
animatePush
иanimatePop
процедуры. Вы должны получить "из вида "и"для просмотра". (Как показано в Примере.)и самое главное, что вы должны сделать, это, действительно,
addSubview
для нового вида "кому".нужно позвонить
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. Используйте его в своем контроллере вида.
обратите внимание, что вы только надо это сделать в" первом " контроллере вида.
ты поп на вершине, ничего не делать. Простой.
Итак, ваш класс...
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) } }
это "так просто": O
использование частных звонков-плохая идея, так как 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];
поскольку это лучший результат в Google, я думал, что поделюсь тем, что я считаю самым разумным способом; который заключается в использовании iOS 7+ transitioning API. Я реализовал это для 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
экземпляр через его initializer. Я делаю это так, что в - х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") контроллеры to и from view будут быть иными. Когда вы находитесь в операции push, контроллер to view будет тем, который будет нажат. Когда вы находитесь в операции pop, контроллер to view будет тем, к которому выполняется переход, а контроллер from view будет тем, который выскакивает.
и
to
контроллер вида должен быть добавлен в качестве подвидаcontainerView
в контексте переходного периода.когда ваша анимация завершится, вы должны позвонить
transitionContext.completeTransition(true)
. Если вы делаете интерактивный переход, вам придется динамически возвратитьBool
доcompleteTransition(didComplete: Bool)
, в зависимости от того, завершен ли переход в конце анимации.наконец (необязательно чтение), вы можете посмотреть, как я сделал переход, над которым я работал. Этот код немного более хаки, и я написал его довольно быстро, поэтому я бы не сказал, что это отличный анимационный код, но он все еще показывает, как сделать анимационную часть.
мой был очень простой переход; я хотел имитировать ту же анимацию, что обычно делает UINavigationController, но вместо анимации "Следующая страница поверх" я хотел реализовать анимацию 1:1 старого контроллера вида одновременно с появлением нового контроллера вида. Это приводит к тому, что два контроллера вида кажутся прикрепленными друг к другу.
для операции push требуется сначала установить
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) })
операция pop в основном обратная. Добавьте
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
есть 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/
Я недавно пытался сделать что-то подобное. Я решил, что мне не нравится скользящая анимация UINavigationController, но я также не хотел делать анимацию, которую UIView дает вам, как curl или что-то в этом роде. Я хотел сделать переход между видами, когда я push или pop.
проблема там заключается в том, что вид буквально удаляет вид или выскакивает один поверх текущего, поэтому исчезать не работает. Решение, к которому я пришел участвовал в принятии моего нового представления и добавлении его в качестве подвида к текущему верхнему виду в стеке UIViewController. Я добавляю его с Альфой 0, а затем делаю кроссфейд. Когда последовательность анимации заканчивается, я нажимаю вид на стек, не анимируя его. Затем я возвращаюсь к старому topView и убираю вещи, которые я изменил.
его немного сложнее, чем это, потому что у вас есть navigationItems вы должны настроить, чтобы сделать переход выглядеть правильно. Кроме того, если вы это сделаете любое вращение, вы должны настроить размеры кадра, как вы добавляете виды в качестве подвидов, чтобы они правильно отображались на экране. Вот некоторые из кода, который я использовал. Я подкласса UINavigationController и отключил пуш и методов поп.
-(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]; }
как я уже упоминал в коде, у меня всегда есть элемент кнопки левой панели, поэтому я не проверяю, равен ли он нулю, прежде чем поместить его в массив, который я передаю в качестве контекста для делегата анимации. Если вы сделаете это, вы можете сделать это проверять.
проблема, которую я нашел, состояла в том, что если вы сбой вообще в методе делегата, он не приведет к сбою программы. Это просто останавливает делегата от завершения, но вы не получите никакого предупреждения.
Так как я делал свою очистку в этой процедуре делегата, это вызывало какое-то странное визуальное поведение, так как оно не заканчивало очистку.кнопка Назад, которую я создаю, вызывает метод "goBack", и этот метод просто вызывает pop обычный.
-(void)goBack { [self popViewControllerAnimated:YES]; }
кроме того, вот моя поп-рутина.
-(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]; } } }
вот и все. Вам понадобится дополнительный код, если вы хотите реализовать ротации. Вам нужно будет установить размер кадра ваших представлений, которые вы добавляете в качестве подвидов, прежде чем показывать их, иначе вы столкнетесь с проблемами ориентации ландшафта, но в последний раз, когда вы видели предыдущий вид, это был портрет. Итак, затем вы добавляете его в качестве подвида и исчезаете, но он отображается как портрет, а затем, когда мы поп без анимации, тот же вид, но тот, который находится в стеке, теперь является пейзажем. Все это выглядит немного пугающе. У всех реализация ротации немного отличается, поэтому я не включил свой код для этого здесь.
надеюсь, что это поможет некоторым людям. Я искал что-то вроде этого и ничего не мог найти. Я не думаю, что это идеальный ответ, но он работает очень хорошо для меня на данный момент.
вот как я сделал то же самое в Swift:
Для Нажмите:
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) })
Для Поп:
Я на самом деле сделал это немного иначе, чем некоторые из ответов выше, но поскольку я новичок в быстром развитии, это может быть неправильно. Я переопределил
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)
на основе
jordanperry
ответ обновлено для swift 4для push
UIViewController
let yourVC = self.storyboard?.instantiateViewController(withIdentifier: "yourViewController") as! yourViewController UIView.animate(withDuration: 0.75, animations: {() -> Void in UIView.setAnimationCurve(.easeInOut) self.navigationController?.pushViewController(terms, animated: true) UIView.setAnimationTransition(.flipFromRight, for: (self.navigationController?.view)!, cache: false) })
Поп
UIView.animate(withDuration: 0.75, animations: {() -> Void in UIView.setAnimationCurve(.easeInOut) UIView.setAnimationTransition(.flipFromLeft, for: (self.navigationController?.view)!, cache: false) }) navigationController?.popViewController(animated: false)
используя ответ iJordan в качестве вдохновения, почему бы просто не создать Категорию на UINavigationController для использования во всем вашем приложении вместо копирования/вставки этого кода анимации повсюду?
UINavigationController+Анимация.h
@interface UINavigationController (Animation) - (void) pushViewControllerWithFlip:(UIViewController*) controller; - (void) popViewControllerWithFlip; @end
UINavigationController+Анимация.м
@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
затем просто импортируйте UINavigationController + анимацию.H файл и назовите его нормально:
[self.navigationController pushViewControllerWithFlip:[[NewViewController alloc] init]]; [self.navigationController popViewControllerWithFlip];
посмотреть ADTransitionController, падение замены для UINavigationController с пользовательскими анимациями перехода (его API соответствует API UINavigationController), который мы создали в Applidium.
вы можете использовать различные предопределенные анимации для push и поп действия, такие как салфетки,Fade,Cube,Карусель,масштабирование и так далее.
хотя все ответы здесь велики и большинство работают очень хорошо, есть немного более простой метод, который достигает того же эффекта...
Для Нажмите:
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]; }];
Для 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]; }];}
Теперь вы можете использовать
UIView.transition
. Обратите внимание, чтоanimated:false
. Это работает с любой опцией перехода, pop, push или Stack replace.if let nav = self.navigationController { UIView.transition(with:nav.view, duration:0.3, options:.transitionCrossDissolve, animations: { _ = nav.popViewController(animated:false) }, completion:nil) }
смотрите мой ответ на этот вопрос для способа сделать это в гораздо меньшем количестве строк кода. Этот метод позволяет анимировать псевдо - "толчок" нового контроллера вида любым удобным вам способом, и когда анимация выполнена, он настраивает навигационный контроллер так же, как если бы вы использовали стандартный метод Push. Мой пример позволяет анимировать слайд-Ин слева или справа. Код повторяется здесь для удобства:
-(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]; }]; }
Я не знаю, каким образом вы можете изменить анимацию перехода публично.
если кнопка "назад" вам не нужна должны использовать модальные контроллеры вида чтобы "оттолкнуться от дна" / "флип" / "в затемнение" / (≥3.2)"загнутая страница" переходы.
на частная стороны, метод
-pushViewController:animated:
вызывает недокументированный метод-pushViewController:transition:forceImmediate:
, Так что, например, если вы хотите перевернуть слева направо переход, вы можно использовать[navCtrler pushViewController:ctrler transition:10 forceImmediate:NO];
вы не можете изменить "поп" переход таким образом,.
из примера приложения, проверьте этот вариант. 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; }
просто использовать:
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];
понимая, что это старый вопрос. Я все еще хотел бы опубликовать этот ответ, так как у меня были некоторые проблемы с появлением нескольких
viewControllers
с предложенными ответами. Мое решение-подклассUINavigationController
и переопределить все методы pop и push.FlippingNavigationController.h
@interface FlippingNavigationController : UINavigationController @end
FlippingNavigationController.м:
#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
Я нашел слегка рекурсивный способ сделать это, который работает для моих целей. У меня есть переменная экземпляра BOOL, которую я использую для блокировки обычной всплывающей анимации и замены моего собственного не анимированного поп-сообщения. Переменная изначально имеет значение нет. Когда кнопка Назад нажата, метод делегата устанавливает его в YES и отправляет новое не анимированное сообщение pop на панель навигации, тем самым вызывая тот же метод делегата снова, на этот раз с переменной, установленной в YES. С переменной установлено значение да, делегат метод устанавливает его в НЕТ и возвращает да, чтобы разрешить не анимированные pop происходят. После того, как второй вызов делегата возвращается, мы возвращаемся в первый, где NO возвращается, блокируя оригинальный анимированный pop! Это на самом деле не так грязно, как кажется. Мой метод shouldPopItem выглядит так:
- (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; } }
работает для меня.