Обмен rootViewController с анимацией - PullRequest
32 голосов
/ 12 января 2012

У меня небольшие проблемы с заменой rootViewControllers на анимацию. Вот код, который я использую:

[UIView transitionWithView:self.window duration:0.8 options:UIViewAnimationOptionTransitionFlipFromRight animations:^{
        self.window.rootViewController = self.navigationController;
    } completion:nil];

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

Ответы [ 6 ]

49 голосов
/ 12 января 2012

transitionWithView предназначен для анимации подпредставлений указанного представления контейнера. Анимировать изменение корневого контроллера представления не так просто. Я потратил много времени, пытаясь сделать это без побочных эффектов. См:

Анимация смены контроллеров вида без использования стека навигационных контроллеров, подпредставлений или модальных контроллеров?

РЕДАКТИРОВАТЬ: добавлена ​​выдержка из ссылочного ответа

[UIView transitionFromView:self.window.rootViewController.view
                    toView:viewController.view
                  duration:0.65f
                   options:transition
                completion:^(BOOL finished){
                    self.window.rootViewController = viewController;
                }];
43 голосов
/ 24 марта 2015

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

Я исправил это, сделав снимок последнего экрана первого контроллера представления, наложив его на второй (целевой) контроллер представления, а затем анимировав его так, как я хотел после Я установил второй контроллер представления как root :

UIView *overlayView = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
[self.destinationViewController.view addSubview:overlayView];
self.window.rootViewController = self.destinationViewController;

[UIView animateWithDuration:0.4f delay:0.0f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
    overlayView.alpha = 0;
} completion:^(BOOL finished) {
    [overlayView removeFromSuperview];
}];

РЕДАКТИРОВАТЬ: Swift 3 версия кода:

let overlayView = UIScreen.main.snapshotView(afterScreenUpdates: false)
destinationViewController.view.addSubview(overlayView)
window.rootViewController = destinationViewController

UIView.animate(withDuration: 0.4, delay: 0, options: .transitionCrossDissolve, animations: {
    overlayView.alpha = 0
}, completion: { finished in
    overlayView.removeFromSuperview()
})
5 голосов
/ 07 ноября 2013

Я нашел transitionWithView: продолжительность: опции: анимация: завершение: для получения более надежного результата.

[UIView transitionWithView:window
                  duration:0.3
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{
                    [fromView removeFromSuperview];
                    [window addSubview:toView];
                    window.rootViewController = toViewController;
                }
                completion:NULL];

Если вы оставите это до блока завершения для установки корневого контроллера представления, то во время таких методов, как просмотр (Will / Did) Появляются

self.view.window.rootViewController

По-прежнему будет установлен предыдущий контроллер вида. Это может не быть проблемой в большинстве ситуаций, если вам не нужно передавать эту ссылку на rootViewController другому коду во время этих методов, как я.

2 голосов
/ 13 февраля 2017

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

Принимая базовую идею cclogg вот моя реализация плавного перехода между двумя контроллерами представления, который не заботится о значениях альфа:

/// Replaces the window's current `rootViewController` with the `newViewController` 
/// passed as a parameter. If `animated`, the window animates the replacement 
/// with a cross dissolve transition.
///
/// - Parameters:
///   - newViewController: The view controller that replaces the current view controller.
///   - animated: Specifies if the transition between the two view controllers is animated.
///   - duration: The transition's duration. (Ignored if `animated == false`)
private func swapCurrentViewController(with newViewController: UIViewController, 
                                       animated: Bool = true, 
                                       duration: TimeInterval = 1) {

    // Get a reference to the window's current `rootViewController` (the "old" one)
    let oldViewController = window?.rootViewController

    // Replace the window's `rootViewController` with the new one
    window?.rootViewController = newViewController

    if animated, let oldView = oldViewController?.view {

        // Add the old view controller's view on top of the new `rootViewController`
        newViewController.view.addSubview(oldView)

        // Remove the old view controller's view in an animated fashion
        UIView.transition(with: window!,
                          duration: duration,
                          options: .transitionCrossDissolve,
                          animations: { oldView.removeFromSuperview() },
                          completion: nil)
    }
}

Важно заменитьrootViewController окна до инициации перехода или анимации, потому что именно так новый контроллер представления узнает свой контекст, то есть правильные поля макета и т. д.


? Это способчтобы пойти, если вам действительно нужно заменить rootViewController окна по любой причине.Тем не менее, я хочу отметить, что с архитектурной точки зрения, на мой взгляд, гораздо лучшим подходом является создание контроллера представления, который будет единолично отвечать за обработку переходов между двумя другими контроллерами представления, и установить его как rootViewController окна итогда никогда не меняй его .Это хорошо объяснено в этом ответе на аналогичный вопрос.

2 голосов
/ 16 июля 2015

Решение, которое сработало для меня, было небольшой модификацией ответа Марко Николовски.В моем существующем контроллере корневого представления был анимированный спиннер, поэтому снимок выглядел странно, потому что он заморозил анимацию.В конце я смог сделать следующее (внутри текущего корневого контроллера представления):

NextRootViewController *nextRootVC = [self.storyboard instantiateViewControllerWithIdentifier:@"NextRootViewController"];

self.view.window.rootViewController = nextRootVC;

[nextRootVC addSubview:self.view];

[UIView animateWithDuration:0.4 delay:0.2 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
    self.view.alpha = 0;
} completion:^(BOOL finished) {
    [self.view removeFromSuperview];
}];
0 голосов
/ 12 января 2012

Вместо замены rootViewControllers, почему бы вам не создать ни пользовательский View, ни, может быть, просто UINavigationController (не показывать заголовок заголовка) и поместить в него «rootViews», которые вы хотите поменять местами.

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