Восстановить анимацию pushViewController для UINavigationController до iOS7

Так. Просто начал переводить свой код IOS на IOS7 и столкнулся с небольшой проблемой.

У меня есть UINavigationController, у которого есть дочерние ViewControllers, и я использую pushViewController для отображения следующих представлений. Чтобы создать параллакс-анимацию с набором изображений, если UINavigationController настроен для анимации набора UIImageViews, и все мои дочерние ViewControllers имеют self.backgroundColor = [UIColor clearColor] прозрачность.

Начиная с iOS7, способ, которым UINavController анимирует дочерние vc, обновляется путем частичного перемещения текущего контроллера представления и, в первую очередь, нажатия нового контроллера представления, моя анимация параллакса выглядит дерьмово. Я вижу, как предыдущий VC немного сдвигается, а затем исчезает. Есть ли способ восстановить предыдущую анимацию UINavigationController pushViewController? Кажется, я не могу найти этого в коде.

WelcomeLoginViewController* welcomeLoginViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"WelcomeLogin"];
    [self.navigationController pushViewController:welcomeLoginViewController animated:YES];    

Даже пробовал использовать:

    [UIView animateWithDuration:0.75
                     animations:^{
                         [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
                         [self.navigationController pushViewController:welcomeLoginViewController animated:NO];
                         [UIView setAnimationTransition:<specific_animation_form> forView:self.navigationController.view cache:NO];
                     }];

Кто-нибудь знает?


person Bart van den Berg    schedule 18.09.2013    source источник
comment
Для записи, стандартная анимация (то есть просто прямое нажатие с анимацией: ДА) теперь демонстрирует такое же поведение.   -  person skantner    schedule 19.09.2013


Ответы (10)


Мне удалось обойти новый тип перехода, создав категорию для UINavigationController. В моем случае мне нужно было вернуть его к старому стилю перехода, потому что у меня есть прозрачные viewControllers, которые скользят по статическому фону.

  • UINavigationController + Retro.h

    @interface UINavigationController (Retro)
    
    - (void)pushViewControllerRetro:(UIViewController *)viewController;
    - (void)popViewControllerRetro;
    
    @end
    
  • UINavigationController + Retro.m

    #import "UINavigationController+Retro.h"
    
    @implementation UINavigationController (Retro)
    
    - (void)pushViewControllerRetro:(UIViewController *)viewController {
        CATransition *transition = [CATransition animation];
        transition.duration = 0.25;
        transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        transition.type = kCATransitionPush;
        transition.subtype = kCATransitionFromRight;
        [self.view.layer addAnimation:transition forKey:nil];
    
        [self pushViewController:viewController animated:NO];
    }
    
    - (void)popViewControllerRetro {
        CATransition *transition = [CATransition animation];
        transition.duration = 0.25;
        transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        transition.type = kCATransitionPush;
        transition.subtype = kCATransitionFromLeft;
        [self.view.layer addAnimation:transition forKey:nil];
    
        [self popViewControllerAnimated:NO];
    }
    
    @end
    
person Arne    schedule 18.09.2013
comment
Привет, Арне, спасибо за решение. Однако вы анимируете представление UINavigationController вместо дочерних представлений контроллера. Таким образом, панель навигации ведет себя иначе. Пытался решить, создав полностью настраиваемые анимации обоих представлений, но все равно не получил желаемого эффекта. - person Bart van den Berg; 19.09.2013
comment
Спасибо, у меня работает Однако, когда пользователь нажимает кнопку «Назад», он возвращается к прерванной анимации (поскольку кнопка «Назад» не вызывает popViewControllerRetro). - person Bill; 03.10.2013
comment
Билл, ты можешь заменить кнопку возврата на leftBarButtonItem. Я выбрал transition.duration = 0.4, потому что казалось, что это ближе к старой скорости, по крайней мере, на симуляторах Xcode. - person jcsahnwaldt Reinstate Monica; 07.10.2013
comment
Это помогло мне - у меня также был viewcontroller с прозрачным фоном, который был помещен в NavigationController с прозрачным фоном и фоновым изображением. Новый переход на iOS7 выглядел как дерьмо, мне интересно, почему Apple не может просто добавить эти новые функции молча, без грубых изменений в собственном поведении пользовательского интерфейса - доставляет нам всем много проблем и головной боли! - person Miros; 14.10.2013
comment
У меня это прекрасно сработало. Я использовал clearColor для своих viewControllers (чтобы получить плавную анимацию внизу в подпредставлениях UIWindow), но получал раздражающее случайное заикание, пока не реализовал это. - person weienw; 01.11.2013
comment
@ Билл: Проверьте мой ответ ниже. - person uerceg; 22.11.2013
comment
Спасибо за это отличное решение ... Я заметил одну вещь: когда я вызываю pushViewControllerRetro с интервалом времени 0,4, я вижу черный цвет во время перехода. Кто-нибудь видел такое поведение? Также простой popViewControler: у меня работает анимация. Так что мне все еще нужно использовать popViewControllerRetro? Спасибо - person iOSAppDev; 31.12.2013
comment
@iOSAppDev, я вижу то же самое! Черный и белый цвета ... Кто-нибудь поправит? - person Martin Herman; 27.05.2014
comment
@Martin Я еще не нашел решения - person iOSAppDev; 27.05.2014
comment
@iOSAppDev Я думаю, мы должны создать для этого новый вопрос ... После замедления примерно до 1 секунды я обнаружил, что это потому, что старый VC мигает белым, а новый тоже. Проверить это. Вы случайно не нашли другое решение? - person Martin Herman; 28.05.2014
comment
@iOSAppDev - я нашел решение. Что я и, вероятно, вы сделали, так это добавили фоновый слой в UINavigationController [_navController.view insertSubview:_someSubview atIndex:0]; ... Теперь то, что я делаю, - это добавляю фоновое представление в UIWindow в качестве подпредставления ... [self.window addSubview:_someView]; и вызываю [self.view setBackgroundColor:[UIColor clearColor]]; в каждом viewDidAppear отправляемого UIViewController . Работает сказочно! :) - person Martin Herman; 31.05.2014

У меня такая же проблема с четкими цветами фона и дрянной анимацией, поэтому я создаю настраиваемый переход для ViewController с новым API iOS7. Все, что вам нужно, это просто установить делегата для вашего контроллера навигации:

// NavigationController does not retain delegate, so you should hold it.
self.navigationController.delegate = self.navigationTransitioningDelegate;

Просто добавьте эти файлы в свой проект: MGNavigationTransitioningDelegate.

person ArtFeel    schedule 28.11.2013
comment
Удивительный! Лучшее решение - person xger86x; 04.07.2014
comment
Работает как шарм! Спасибо! - person jestro; 13.02.2015
comment
Очень полезно! Спасибо! - person Sagar D; 03.05.2016
comment
К сожалению, это решение нарушает автоматическое размещение - когда вы вращаете и возвращаетесь, все виды на предыдущей странице прерываются, пока вы снова не повернетесь. - person Tom; 19.05.2016
comment
Хорошее решение. Я создал версию вашей сути для Swift 3: gist.github.com/steryokhin/f136bd3e11d142530b0 - person Sergey Teryokhin; 08.03.2017

У меня была проблема, когда, когда UIViewController A выполнял pushViewController, чтобы подтолкнуть UIViewController B, анимация push остановилась примерно на 25%, остановилась, а затем сдвинула B до конца.

Этого НЕ было на iOS 6, но как только я начал использовать iOS 7 в качестве базового SDK в XCode 5, это начало происходить.

Исправление заключается в том, что в контроллере представления B не было задано backgroundColor в его корневом представлении (корневое представление - это то, которое является значением viewController.view, которое вы обычно устанавливаете в loadView). Установка backgroundColor в инициализаторе этого корневого представления устранила проблему.

Мне удалось это исправить следующим образом:

// CASE 1: The root view for a UIViewController subclass that had a halting animation

- (id)initWithFrame:(CGRect)frame

{

     if ((self = [super initWithFrame:frame])) {

          // Do some initialization ...

          // self.backgroundColor was NOT being set

          // and animation in pushViewController was slow and stopped at 25% and paused

     }

     return self;

}

// CASE 2: HERE IS THE FIX

- (id)initWithFrame:(CGRect)frame

{

     if ((self = [super initWithFrame:frame])) {

          // Do some initialization ...

          // Set self.backgroundColor for the fix!

          // and animation in pushViewController is no longer slow and and no longer stopped at 25% and paused

          self.backgroundColor = [UIColor whiteColor]; // or some other non-clear color

     }

     return self;

}
person Joel    schedule 02.10.2013
comment
У меня точно такая же проблема, но, к сожалению, в моем представлении B есть clearColor, мне просто нужно добавить точно такой же фон изображения, что и self.parentViewController.view.xxxxxx, и теперь он работает ... спасибо! - person mongeta; 02.10.2013
comment
Да, это точно устранило проблему. Спасибо! - person Bill Cheswick; 13.10.2013
comment
Безумное решение. У меня тоже работало! - person CedricSoubrie; 22.01.2014
comment
Как ты это получил? я мог бы попробовать Вуду вместо этого .. спасибо - person amar; 16.07.2014

Во-первых, я не использую раскадровку. Я пробовал использовать UINavigationController + Retro. По какой-то причине UINavigationController с трудом освобождает UIViewController наверху стека. Вот решение, которое работает для меня с использованием настраиваемого перехода iOS 7.

  1. Установите делегата на себя.

    navigationController.delegate = self;
    
  2. Объявите этот UINavigationControllerDelegate.

    - (id<UIViewControllerAnimatedTransitioning>)navigationController (UINavigationController *)navigationController 
    animationControllerForOperation:(UINavigationControllerOperation)operation
    fromViewController:(UIViewController *)fromVC 
    toViewController:(UIViewController *)toVC 
    {
        TransitionAnimator *animator = [TransitionAnimator new]; 
        animator.presenting = YES;
        return animator;
    }
    

    Обратите внимание, что он будет вызываться, только если для параметра Animation установлено значение YES. Например

    [navigationController pushViewController:currentViewController animated:YES];
    
  3. Создайте класс аниматора, расширяющий NSObject. Я назвал свой TransitionAnimator, который был модифицирован из TLTransitionAnimator TeehanLax внутри UIViewController-Transitions-Example.

    TransitionAnimator.h

    #import <Foundation/Foundation.h>
    @interface TransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning>
    @property (nonatomic, assign, getter = isPresenting) BOOL presenting;
    @end
    

    TransitionAnimator.m

    #import "TransitionAnimator.h"
    @implementation TransitionAnimator
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
        return 0.5f;
    }
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
        UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];    
        if (self.presenting) {
            //ANIMATE VC ENTERING FROM THE RIGHT SIDE OF THE SCREEN
            [transitionContext.containerView addSubview:fromVC.view];
            [transitionContext.containerView addSubview:toVC.view];     
            toVC.view.frame = CGRectMake(0, 0, 2*APP_W0, APP_H0); //SET ORIGINAL POSITION toVC OFF TO THE RIGHT                
            [UIView animateWithDuration:[self transitionDuration:transitionContext] 
                animations:^{
                    fromVC.view.frame = CGRectMake(0, (-1)*APP_W0, APP_W0, APP_H0); //MOVE fromVC OFF TO THE LEFT
                    toVC.view.frame = CGRectMake(0, 0, APP_W0, APP_H0); //ANIMATE toVC IN TO OCCUPY THE SCREEN
                } completion:^(BOOL finished) {
                    [transitionContext completeTransition:YES];
                }];
        }else{
            //ANIMATE VC EXITING TO THE RIGHT SIDE OF THE SCREEN
        }
    }
    @end
    

    Используйте флаг представления, чтобы установить направление, которое вы хотите анимировать, или какое-либо условие, которое вы предпочитаете. Вот ссылка на справочник Apple.

person voratima    schedule 08.10.2013
comment
Здорово. Одно дело - не использовать переменную представления. UINavigationControllerOperation указывает, что это операция Push или Pop. - person ttotto; 08.04.2014

Спасибо, ребята, за отзыв. Нашел решение в полном воссоздании поведения UINavigationController. Когда я почти закончил, я наткнулся на решение Ника Локвуда:

https://github.com/nicklockwood/OSNavigationController

OSNavigationController - это повторная реализация UINavigationController с открытым исходным кодом. В настоящее время он включает только часть функций UINavigationController, но долгосрочная цель состоит в том, чтобы воспроизвести 100% функций.

OSNavigationController на самом деле не предназначен для использования как есть. Идея состоит в том, что вы можете выполнить его форк, а затем легко настроить его внешний вид и поведение в соответствии с любыми особыми требованиями, которые могут быть у вашего приложения. Настроить OSNavigationController намного проще, чем пытаться настроить UINavigationController, поскольку код открыт, и вам не нужно беспокоиться о частных методах, недокументированном поведении или изменениях реализации между версиями.

Переопределив свой UINavigationController его кодом, я смог работать с фоновыми изображениями в UINavigationcontroller.

Спасибо!

person Bart van den Berg    schedule 24.09.2013

Просто добавьте:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

Этот:

[[self window] setBackgroundColor:[UIColor whiteColor]];

Конечный результат:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions (NSDictionary *)launchOptions {    
    [[self window] setBackgroundColor:[UIColor whiteColor]];

    // Override point for customization after application launch.
    return YES;
}
person Riko87    schedule 03.07.2014

По-видимому, в iOS7 есть новый способ определения ваших собственных пользовательских переходов UIViewController. Посмотрите документацию на UIViewControllerTransitioningDelegate. Кроме того, вот ссылка на статью об этом: http://www.doubleencore.com/2013/09/ios-7-custom-transitions/

person John Jacecko    schedule 24.09.2013
comment
Если я не ошибаюсь, похоже, это работает только с представлением контроллера представления. Я не мог заставить его работать при нажатии контроллера представления на контроллер навигации. - person GenesisST; 03.10.2013

Swift 5 реализация ответа Арне:

extension UINavigationController {
  func pushViewControllerLegacyTransition(_ viewController: UIViewController) {
    let transition = CATransition()
    transition.duration = 0.25
    transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
    transition.type = .push
    transition.subtype = .fromRight
    view.layer.add(transition, forKey: nil)
    pushViewController(viewController, animated: false)
  }

  func popViewControllerLegacyTransition() {
    let transition = CATransition()
    transition.duration = 0.25
    transition.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
    transition.type = .push
    transition.subtype = .fromLeft
    view.layer.add(transition, forKey: nil)
    popViewController(animated: false)
  }
}
person faircloud    schedule 04.06.2020

Нашел еще один отличный ресурс, который может помочь:

http://www.teehanlax.com/blog/custom-uiviewcontroller-transitions

Использование iOS7 TLTransitionAnimator для создания пользовательской анимации

person Bart van den Berg    schedule 27.09.2013

Я проголосовал за ответ @Arne, потому что считаю его самым элегантным решением этой проблемы. Я просто хотел бы добавить код, чтобы ответить на проблему @Bill из его комментария к решению @Arne. Вот цитата из комментария:

Спасибо, у меня работает Однако, когда пользователь нажимает кнопку «Назад», он возвращается к прерванной анимации (поскольку кнопка «Назад» не вызывает popViewControllerRetro). - Билл 3 октября в 12:36

Чтобы вызвать popViewControllerRetro при нажатии кнопки «Назад», вы можете выполнить небольшой прием, чтобы добиться этого. Войдите в свой push-контроллер представления, импортируйте UIViewController + Retro.h и добавьте этот код в свой метод viewWillDisappear:

- (void)viewWillDisappear:(BOOL)animated {
    if ([self.navigationController.viewControllers indexOfObject:self] == NSNotFound) {
        [self.navigationController popViewControllerRetro];
    }

    [super viewWillDisappear:animated];
}

Этот оператор if обнаружит нажатие кнопки «Назад» и вызовет popViewControllerRetro из класса категории.

С наилучшими пожеланиями.

person uerceg    schedule 22.11.2013