Анімація переходу перемикача RootViewController


125

Чи є спосіб отримати ефект переходу / анімації, замінивши існуючий контролер перегляду як rootviewcontroller на новий в appDelegate?

Відповіді:


272

Можна переключити комутацію rootViewControllerв блоці анімації з переходом:

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{ self.window.rootViewController = newViewController; }
                completion:nil];

5
Ей Оле, я спробував цей підхід, він працював частково, річ у моєму додатку залишатиметься лише в ландшафтному режимі, але, роблячи перехід rootviewcontroller, нещодавно представлений контролер подання завантажується в портрет на початку і швидко повертається в ландшафтний режим , як це вирішити?
Кріс Чен

4
Я відповів на питання Кріса Чена (сподіваюсь! Можливо?) У своєму окремому запитанні тут: stackoverflow.com/questions/8053832/…
Kalle

1
ей, я хочу натиснути перехід в тій же анімації, чи можу я цього досягти?
Користувач 1531343

14
Я помітив деякі проблеми з цим, а саме неправильні елементи / ліниво завантажені елементи. Наприклад, якщо у вас немає панелі навігації на існуючому кореневому vc, тоді анімуйте новий VC, який має такий, анімація завершується, а потім додається панель навігації. Це виглядає ніби нерозумно - будь-які думки щодо того, чому це може бути, і що можна зробити?
anon_dev1234

1
Я виявив, що виклик newViewController.view.layoutIfNeeded()перед блоком анімації виправляє проблеми з ліниво завантаженими елементами.
Whoa

66

Я знайшов це і працює чудово:

у вашому додаткуДелегат:

- (void)changeRootViewController:(UIViewController*)viewController {

    if (!self.window.rootViewController) {
        self.window.rootViewController = viewController;
        return;
    }

    UIView *snapShot = [self.window snapshotViewAfterScreenUpdates:YES];

    [viewController.view addSubview:snapShot];

    self.window.rootViewController = viewController;

    [UIView animateWithDuration:0.5 animations:^{
        snapShot.layer.opacity = 0;
        snapShot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5);
    } completion:^(BOOL finished) {
        [snapShot removeFromSuperview];
    }];
}

у вашому додатку

 if (!app) { app = (AppDelegate *)[[UIApplication sharedApplication] delegate]; }
        [app changeRootViewController:newViewController];

кредити:

https://gist.github.com/gimenete/53704124583b5df3b407


Чи обертається ця підтримка автоматичного екрана?
Wingzero

1
Це рішення працювало краще в моєму випадку. При використанні translationWithView новий контролер кореневого виду був правильно викладений до завершення переходу. Такий підхід дозволяє новому контролеру кореневого перегляду додати у вікно, викласти та перенести його.
Фоста

@Wingzero трохи запізнився, але це дозволить будь-який перехід, або через UIView.animations (скажімо, CGAffineTransform з обертанням), або користувацьким CAAnimation.
Може

41

Я розміщую відповідь Ісуса, реалізовану швидко. Він бере аргумент ідентифікатора viewcontroller як аргумент, завантажує з раскадровки бажаний ViewVeController і змінює rootViewController з анімацією.

Оновлення Swift 3.0:

  func changeRootViewController(with identifier:String!) {
    let storyboard = self.window?.rootViewController?.storyboard
    let desiredViewController = storyboard?.instantiateViewController(withIdentifier: identifier);

    let snapshot:UIView = (self.window?.snapshotView(afterScreenUpdates: true))!
    desiredViewController?.view.addSubview(snapshot);

    self.window?.rootViewController = desiredViewController;

    UIView.animate(withDuration: 0.3, animations: {() in
      snapshot.layer.opacity = 0;
      snapshot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5);
      }, completion: {
        (value: Bool) in
        snapshot.removeFromSuperview();
    });
  }

Оновлення Swift 2.2:

  func changeRootViewControllerWithIdentifier(identifier:String!) {
    let storyboard = self.window?.rootViewController?.storyboard
    let desiredViewController = storyboard?.instantiateViewControllerWithIdentifier(identifier);

    let snapshot:UIView = (self.window?.snapshotViewAfterScreenUpdates(true))!
    desiredViewController?.view.addSubview(snapshot);

    self.window?.rootViewController = desiredViewController;

    UIView.animateWithDuration(0.3, animations: {() in
      snapshot.layer.opacity = 0;
      snapshot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5);
      }, completion: {
        (value: Bool) in
        snapshot.removeFromSuperview();
    });
  }

  class func sharedAppDelegate() -> AppDelegate? {
    return UIApplication.sharedApplication().delegate as? AppDelegate;
  }

Після цього у вас є дуже просте використання з будь-якого місця:

let appDelegate = AppDelegate.sharedAppDelegate()
appDelegate?.changeRootViewControllerWithIdentifier("YourViewControllerID")

Швидке оновлення 3.0

let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.changeRootViewController(with: "listenViewController")

25

Швидкий 2

UIView.transitionWithView(self.window!, duration: 0.5, options: UIViewAnimationOptions.TransitionFlipFromLeft, animations: {
  self.window?.rootViewController = anyViewController
}, completion: nil)

Швидкий 3, 4, 5

UIView.transition(with: self.window!, duration: 0.5, options: UIView.AnimationOptions.transitionFlipFromLeft, animations: {
  self.window?.rootViewController = anyViewController
}, completion: nil)

XCode виправив мій код так: `` `UIView.transition (з: self.view.window !, тривалість: 0,5, параметри: UIViewAnimationOptions.transitionFlipFromTop, анімації: {appDelegate.window? .RootViewController = myViewController}, завершення: nil) `` `
scaryguy

10

просто спробуйте це. Добре працює для мене.

BOOL oldState = [UIView areAnimationsEnabled];
[UIView setAnimationsEnabled:NO];
self.window.rootViewController = viewController;
[UIView transitionWithView:self.window duration:0.5 options:transition animations:^{
    //
} completion:^(BOOL finished) {
    [UIView setAnimationsEnabled:oldState];
}];

Редагувати:

Цей краще.

- (void)setRootViewController:(UIViewController *)viewController
               withTransition:(UIViewAnimationOptions)transition
                   completion:(void (^)(BOOL finished))completion {
    UIViewController *oldViewController = self.window.rootViewController;
    [UIView transitionFromView:oldViewController.view 
                        toView:viewController.view
                      duration:0.5f
                       options:(UIViewAnimationOptions)(transition|UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionLayoutSubviews)
                    completion:^(BOOL finished) {
        self.window.rootViewController = viewController;
        if (completion) {
            completion(finished);
        }
    }];
}

У мене була дивна анімація за замовчуванням, коли просто перемикав root VC. Перша версія позбулася цього для мене.
juhan_h

Друга версія буде оживляти макет субпрезентації, про який згадував juhan_h. Якщо цього не потрібно, або експериментуйте з видаленням UIViewAnimationOptionAllowAnimatedContent|UIViewAnimationOptionLayoutSubviews, або використовуйте першу версію, або якийсь інший метод.
ftvs

3

Щоб потім не виникнути проблем з переходом переходу в додатку, добре також очистити старий вигляд зі стека

UIViewController *oldController=self.window.rootViewController;

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionCrossDissolve
                animations:^{ self.window.rootViewController = nav; }
                completion:^(BOOL finished) {
                    if(oldController!=nil)
                        [oldController.view removeFromSuperview];
                }];

2

Правильна відповідь - вам не потрібно замінювати rootViewControllerна своєму вікні. Натомість створіть користувальницький UIViewController, призначте його один раз і дозвольте йому відображати один дочірній контролер за один раз та замініть його на анімацію, якщо потрібно. Ви можете використовувати наступний фрагмент коду як вихідний пункт:

Swift 3.0

import Foundation
import UIKit

/// Displays a single child controller at a time.
/// Replaces the current child controller optionally with animation.
class FrameViewController: UIViewController {

    private(set) var displayedViewController: UIViewController?

    func display(_ viewController: UIViewController, animated: Bool = false) {

        addChildViewController(viewController)

        let oldViewController = displayedViewController

        view.addSubview(viewController.view)
        viewController.view.layoutIfNeeded()

        let finishDisplay: (Bool) -> Void = {
            [weak self] finished in
            if !finished { return }
            oldViewController?.view.removeFromSuperview()
            oldViewController?.removeFromParentViewController()
            viewController.didMove(toParentViewController: self)
        }

        if (animated) {
            viewController.view.alpha = 0
            UIView.animate(
                withDuration: 0.5,
                animations: { viewController.view.alpha = 1; oldViewController?.view.alpha = 0 },
                completion: finishDisplay
            )
        }
        else {
            finishDisplay(true)
        }

        displayedViewController = viewController
    }

    override var preferredStatusBarStyle: UIStatusBarStyle {
        return displayedViewController?.preferredStatusBarStyle ?? .default
    }
}

І спосіб його використання:

...
let rootController = FrameViewController()
rootController.display(UINavigationController(rootViewController: MyController()))
window.rootViewController = rootController
window.makeKeyAndVisible()
...

Наведений вище приклад демонструє, що можна гніздитися UINavigationControllerвсередині, FrameViewControllerі це працює чудово. Такий підхід забезпечує високий рівень налаштування та контролю. Просто зателефонуйте FrameViewController.display(_)будь-коли, коли ви захочете замінити кореневий контролер у вашому вікні, і він зробить цю роботу за вас.


2

Це оновлення для швидкого 3, цей спосіб повинен бути у вашого делегата програми, і ви викликаєте його з будь-якого контролера перегляду через спільний екземпляр делегата програми

func logOutAnimation() {
    let storyBoard = UIStoryboard.init(name: "SignIn", bundle: nil)
    let viewController = storyBoard.instantiateViewController(withIdentifier: "signInVC")
    UIView.transition(with: self.window!, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromLeft, animations: {
        self.window?.rootViewController = viewController
        self.window?.makeKeyAndVisible()
    }, completion: nil)
}

Частина, яка відсутня в різних питаннях вище, є

    self.window?.makeKeyAndVisible()

Сподіваюся, що це комусь допоможе.


1

в AppDelegate.h:

#define ApplicationDelegate ((AppDelegate *)[UIApplication sharedApplication].delegate)]

у вашому контролері:

[UIView transitionWithView:self.window
                  duration:0.5
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^{
    ApplicationDelegate.window.rootViewController = newViewController;
    }
                completion:nil];

6
Це те саме, що прийнята відповідь, за винятком того, що форматування неправильне. Навіщо турбуватися?
jrturton

1
Цей не залежить від того, чи перебуваєте ви в View чи ViewController. Найбільша різниця є більш філософською з точки зору того, наскільки товстими чи тонкими ви любите свої представлення та ViewControllers.
Макс

0

Я пропоную свій спосіб, який він чудово працює в моєму проекті, і він пропонує мені гарну анімацію. Я перевірив інші пропозиції, знайдені в цій публікації, але деякі з них працюють не так, як очікувалося.

- (void)transitionToViewController:(UIViewController *)viewController withTransition:(UIViewAnimationOptions)transition completion:(void (^)(BOOL finished))completion {
// Reset new RootViewController to be sure that it have not presented any controllers
[viewController dismissViewControllerAnimated:NO completion:nil];

[UIView transitionWithView:self.window
                  duration:0.5f
                   options:transition
                animations:^{
                    for (UIView *view in self.window.subviews) {
                        [view removeFromSuperview];
                    }
                    [self.window addSubview:viewController.view];

                    self.window.rootViewController = viewController;
                } completion:completion];
}

0

Приємна мила анімація (протестована на Swift 4.x):

extension AppDelegate {
   public func present(viewController: UIViewController) {
        guard let window = window else { return }
        UIView.transition(with: window, duration: 0.5, options: .transitionFlipFromLeft, animations: {
            window.rootViewController = viewController
        }, completion: nil)
    }
}

Подзвоніть з

guard let delegate = UIApplication.shared.delegate as? AppDelegate else { return }
delegate.present(viewController: UIViewController())
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.