RootViewController 스위치 전환 애니메이션


125

기존 viewcontroller를 rootviewcontroller로 appDelegate에서 새로운 것으로 바꾸면서 전환 / 애니메이션 효과를 줄 수있는 방법이 있습니까?

답변:


272

rootViewController전환 애니메이션 블록에서 전환을 래핑 할 수 있습니다 .

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

5
안녕하세요 올레,이 접근법을 부분적으로 시도했지만 부분적으로 작동했습니다. 내 앱은 가로 모드에서만 유지되지만 rootviewcontroller 전환을 수행하면 새로 표시된보기 컨트롤러가 처음에 세로로로드되고 가로 모드로 빠르게 회전합니다. 어떻게 해결할 수 있습니까?
Chris Chen

4
나는 그의 별도의 질문에서 Chris Chen의 질문에 (아마도! 아마?) 대답했습니다 : 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
이 솔루션은 제 경우에 더 잘 작동했습니다. transitionWithView를 사용할 때 새 루트 뷰 컨트롤러는 전환이 완료 될 때까지 올바르게 배치되었습니다. 이 방법을 사용하면 새로운 루트 뷰 컨트롤러를 창에 추가하고 배치 한 다음 전환 할 수 있습니다.
Fostah

@Wingzero는 조금 늦었지만 UIView.animations (예 : 회전하는 CGAffineTransform) 또는 사용자 정의 CAAnimation을 통해 모든 종류의 전환이 가능합니다.

41

나는 예수의 답변을 신속하게 이행하고 있습니다. 뷰 컨트롤러의 식별자를 인수로 가져와 스토리 보드에서 원하는 뷰 컨트롤러에서로드하고 애니메이션으로 rootViewController를 변경합니다.

스위프트 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();
    });
  }

스위프트 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는 다음과 같이 내 코드를 수정했습니다. ```
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);
        }
    }];
}

단순히 루트 VC를 전환 할 때 이상한 기본 애니메이션이있었습니다. 첫 번째 버전은 저를 위해 제거되었습니다.
juhan_h 2016 년

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창에서 를 교체 할 필요가 없다는 것 입니다. 대신 custom을 만들고 한 UIViewController번 할당 한 다음 한 번에 하나의 자식 컨트롤러를 표시하고 필요한 경우 애니메이션으로 바꾸십시오. 다음 코드를 시작점으로 사용할 수 있습니다.

스위프트 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에 의존하지 않습니다. 가장 큰 차이점은 View와 ViewController가 얼마나 두껍거나 얇은 지에 대한 철학적입니다.
Max

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())
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.