Как изменить анимацию 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
UIViewControllerlet 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; @endUINavigationController+Анимация.м
@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 @endFlippingNavigationController.м:
#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; } }работает для меня.