Как изменить анимацию 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];
и то же самое для push..
версия 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];
Для Pop:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.75];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
[UIView commitAnimations];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelay:0.375];
[self.navigationController popViewControllerAnimated:NO];
[UIView commitAnimations];
Я все еще получаю много отзывов от этого, поэтому я собираюсь продолжить и обновить его, чтобы использовать анимационные блоки, которые в любом случае рекомендуются 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];
}];
Для Pop:
[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).
делая в segue, последние две строки следует заменить на:
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) перехода, которые можно легко использовать. Это: "crossfade"и " flip". Теперь два самых бесполезных перехода во всех вычислениях - "crossfade" и "flip", которые никто не использует. Если вы хотите делать самые обычные переходы, например "соскальзывать", вам предстоит проделать огромный объем работы. Эта работа, объясняется в этом посте!
во-первых, если вы хотите использовать один из двух анимов, которые предоставляет Apple (crossfade, flips), это просто - используйте решение @PeterDeWeese выше.
во-вторых, есть старый быстрый-и-грязный CATransition quickfix. это объясняется подробно здесь. Это действительно не работает и не является реалистичным решением.
в противном случае, удивительно, вы должны приложить усилия, чтобы сделать полный пользовательский переход.
повторю:
даже если вы просто хотите самый простой, наиболее распространенный, переход перемещения/перемещения, к лучшему или худшему, вам нужно реализовать полный пользовательский переход.
вот как это сделать он. ..
1. Вам нужен пользовательский UIViewControllerAnimatedTransitioning
вам нужен собственный bool, как
popStyle
. (Он появляется или исчезает?)необходимо указать
transitionDuration
(тривиальный) и основной вызов,animateTransition
на самом деле вам нужно будет написать две разные процедуры для inside
animateTransition
. Один для толчка и для поп. Вероятно, назовите ихanimatePush
иanimatePop
. ВнутриanimateTransition
, просто ветка наpopStyle
к двум процедурампример ниже делает простой шаг-за переездом-офф
в своем
animatePush
иanimatePop
процедуры. Вы должны получить "from view "и"to view". (Как показано в Примере.)и самое главное, что вы должны сделать, это, действительно,
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. Используйте его в контроллере view.
обратите внимание, что вы только сделать это в контроллере" первого " вида.
ты поп на вершине, ничего не делать. Простой.
Итак, ваш класс...
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.
это довольно просто объединить это с how 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. Я делаю это так, что в 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) контроллеры to и from view будут быть иными. Когда вы находитесь в операции толчка, для просмотра контроллер будет один толкают. Когда вы находитесь в операции pop, контроллер для просмотра будет тем, который переходит, а контроллер из вида будет тем, который выскакивает.
и to
контроллер вида должен быть добавлен в качестве подвида containerView
в контексте переходного периода.
когда ваша анимация завершится, вы должны позвонить transitionContext.completeTransition(true)
. Если вы делаете интерактивный переход, вам придется динамически возвращать Bool
to completeTransition(didComplete: Bool)
, в зависимости от того, завершен ли переход в конце анимации.
наконец (необязательно чтение), вы можете посмотреть, как я сделал переход, над которым работал. Этот код немного более хакерский, и я написал его довольно быстро, поэтому я бы не сказал, что это отличный анимационный код, но он все еще показывает, как сделать часть анимации.
мой был действительно простой переход; я хотел имитировать ту же анимацию, что обычно делает UINavigationController, но вместо анимации "Следующая страница сверху", я хотел реализовать анимацию 1:1 старого контроллера вида одновременно с появлением нового контроллера вида. Это приводит к тому, что два контроллера вида кажутся прикрепленными друг к другу.
для операции push, которая требует первой установки toViewController
начало взгляда С на оси с экрана, добавляя это как subview 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 дает вам как завиток или что-то в этом роде. Я хотел сделать крест между видами, когда я нажимаю или поп.
проблема заключается в том, что представление буквально удаляет представление или выскакивает поверх текущего, поэтому исчезание не работает. Решение, к которому я пришел участвовал в принятии моего нового представления и добавлении его в качестве подвида к текущему верхнему виду в стеке UIViewController. Я добавляю его с Альфой 0, затем делаю crossfade. Когда последовательность анимации заканчивается, я нажимаю вид на стек, не анимируя его. Затем я возвращаюсь к старому topView и убираю вещи, которые я изменил.
его немного сложнее, чем это, потому что у вас есть navigationItems вы должны настроить, чтобы сделать переход выглядеть правильно. Кроме того, если вы это сделаете любое вращение, вы должны настроить размеры кадра, как вы добавляете представления в качестве подвидов, чтобы они правильно отображались на экране. Вот некоторые из кодов, которые я использовал. Я подкласс UINavigationController и преодолел методы push и pop.
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
UIViewController *currentViewController = [self.viewControllers lastObject];
//if we don't have a current controller, we just do a normal push
if(currentViewController == nil)
{
[super pushViewController:viewController animated:animated];
return;
}
//if no animation was requested, we can skip the cross fade
if(!animation)
{
[super pushViewController:viewController animated:NO];
return;
}
//start the cross fade. This is a tricky thing. We basically add the new view
//as a subview of the current view, and do a cross fade through alpha values.
//then we push the new view on the stack without animating it, so it seemlessly is there.
//Finally we remove the new view that was added as a subview to the current view.
viewController.view.alpha = 0.0;
//we need to hold onto this value, we'll be releasing it later
NSString *title = [currentViewController.title retain];
//add the view as a subview of the current view
[currentViewController.view addSubview:viewController.view];
[currentViewController.view bringSubviewToFront:viewController.view];
UIBarButtonItem *rButtonItem = currentViewController.navigationItem.rightBarButtonItem;
UIBarButtonItem *lButtonItem = currentViewController.navigationItem.leftBarButtonItem;
NSArray *array = nil;
//if we have a right bar button, we need to add it to the array, if not, we will crash when we try and assign it
//so leave it out of the array we are creating to pass as the context. I always have a left bar button, so I'm not checking to see if it is nil. Its a little sloppy, but you may want to be checking for the left BarButtonItem as well.
if(rButtonItem != nil)
array = [[NSArray alloc] initWithObjects:currentViewController,viewController,title,lButtonItem,rButtonItem,nil];
else {
array = [[NSArray alloc] initWithObjects:currentViewController,viewController,title,lButtonItem,nil];
}
//remove the right bar button for our transition
[currentViewController.navigationItem setRightBarButtonItem:nil animated:YES];
//remove the left bar button and create a backbarbutton looking item
//[currentViewController.navigationItem setLeftBarButtonItem:nil animated:NO];
//set the back button
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:title style:kButtonStyle target:self action:@selector(goBack)];
[currentViewController.navigationItem setLeftBarButtonItem:backButton animated:YES];
[viewController.navigationItem setLeftBarButtonItem:backButton animated:NO];
[backButton release];
[currentViewController setTitle:viewController.title];
[UIView beginAnimations:@"push view" context:array];
[UIView setAnimationDidStopSelector:@selector(animationForCrossFadePushDidStop:finished:context:)];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.80];
[viewController.view setAlpha: 1.0];
[UIView commitAnimations];
}
-(void)animationForCrossFadePushDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
UIViewController *c = [(NSArray*)context objectAtIndex:0];
UIViewController *n = [(NSArray*)context objectAtIndex:1];
NSString *title = [(NSArray *)context objectAtIndex:2];
UIBarButtonItem *l = [(NSArray *)context objectAtIndex:3];
UIBarButtonItem *r = nil;
//not all views have a right bar button, if we look for it and it isn't in the context,
//we'll crash out and not complete the method, but the program won't crash.
//So, we need to check if it is there and skip it if it isn't.
if([(NSArray *)context count] == 5)
r = [(NSArray *)context objectAtIndex:4];
//Take the new view away from being a subview of the current view so when we go back to it
//it won't be there anymore.
[[[c.view subviews] lastObject] removeFromSuperview];
[c setTitle:title];
[title release];
//set the search button
[c.navigationItem setLeftBarButtonItem:l animated:NO];
//set the next button
if(r != nil)
[c.navigationItem setRightBarButtonItem:r animated:NO];
[super pushViewController:n animated:NO];
}
как я упоминаю в коде, у меня всегда есть элемент кнопки левой панели, поэтому я не проверяю, равен ли он нулю, прежде чем поместить его в массив, который я передаю как контекст для делегата анимации. Если вы сделаете это, вы можете сделать это проверять.
проблема, которую я нашел, заключалась в том, что если вы вообще сбой в методе делегата, он не приведет к сбою программы. Это просто останавливает делегата от завершения, но вы не получаете никакого предупреждения.
Так как я делал очистку в этой процедуре делегата, это вызывало какое-то странное визуальное поведение, так как это не заканчивало очистку.
кнопка "Назад", которую я создаю, вызывает метод "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)
})
Для Pop:
Я на самом деле сделал это немного иначе, чем некоторые из ответов выше, но поскольку я новичок в быстром развитии, это может быть неправильно. Я отменил 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.
вы можете использовать различные предопределенные анимации для пуш и поп действия, такие как салфетки, Fade, куб, Карусель, масштабирование и так далее.
хотя все ответы здесь велики и большинство работают очень хорошо, есть немного более простой метод, который достигает того же эффекта...
Для Нажмите:
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)
}
смотрите мой ответ на этот вопрос для способа сделать это в гораздо меньшем количестве строк кода. Этот метод позволяет анимировать псевдо - "толчок" нового контроллера вида любым способом, и когда анимация будет выполнена, он настраивает навигационный контроллер так же, как если бы вы использовали стандартный метод толчка. Мой пример позволяет анимировать слайд-Ин слева или справа. Код повторяется здесь для удобства:
-(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];
}];
}
Я не знаю, каким образом вы можете изменить анимацию перехода публично.
Если кнопка "назад" вам не нужна должны использовать модальные контроллеры вида чтобы иметь переходы" push from bottom " / "flip" / "fade" / (≥3.2)" Page curl".
на частная стороны, метод -pushViewController:animated:
вызывает недокументированный метод -pushViewController:transition:forceImmediate:
, Так например, если вы хотите флип-слева-направо переход, вы может использовать
[navCtrler pushViewController:ctrler transition:10 forceImmediate:NO];
вы не можете изменить переход "pop" таким образом.
из примера приложения, проверить этот вариант. 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, которую я использую, чтобы заблокировать обычную анимацию popping и заменить мое собственное не анимированное поп-сообщение. Переменная изначально имеет значение NO. Когда кнопка "Назад" нажата, метод делегата устанавливает его в да и отправляет новое не анимированное 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;
}
}
работает для меня.