programing

rootViewController를 애니메이션과 스왑하시겠습니까?

testmans 2023. 8. 13. 09:35
반응형

rootViewController를 애니메이션과 스왑하시겠습니까?

앱 대리인을 통해 탭 모음이 있는 다른 루트 뷰 컨트롤러로 스왑하려고 하는데 전환 애니메이션을 추가하려고 합니다.기본적으로 애니메이션 없이 보기만 표시됩니다.

let tabBar = self.instantiateViewController(storyBoard: "Main", viewControllerID: "MainTabbar")
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window = UIWindow(frame: UIScreen.main.bounds)
appDelegate.window?.rootViewController = tabBar
appDelegate.window?.makeKeyAndVisible()

그렇게 해서 다른 루트뷰 컨트롤러로 스왑했습니다.

사용할 수 있습니다.UIView.transition(with: view)대체하기 위해rootViewController상당한UIWindow:

guard let window = UIApplication.shared.keyWindow else {
    return
}

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "MainTabbar")

// Set the new rootViewController of the window.
// Calling "UIView.transition" below will animate the swap.
window.rootViewController = vc

// A mask of options indicating how you want to perform the animations.
let options: UIView.AnimationOptions = .transitionCrossDissolve

// The duration of the transition animation, measured in seconds.
let duration: TimeInterval = 0.3

// Creates a transition animation.
// Though `animations` is optional, the documentation tells us that it must not be nil. ¯\_(ツ)_/¯
UIView.transition(with: window, duration: duration, options: options, animations: {}, completion:
{ completed in
    // maybe do something on completion here
})

스위프트 4

다음에 함수 붙여넣기AppDelegate:

func setRootViewController(_ vc: UIViewController, animated: Bool = true) {
    guard animated, let window = self.window else {
        self.window?.rootViewController = vc
        self.window?.makeKeyAndVisible()
        return
    }

    window.rootViewController = vc
    window.makeKeyAndVisible()
    UIView.transition(with: window,
                      duration: 0.3,
                      options: .transitionCrossDissolve,
                      animations: nil,
                      completion: nil)
}

다른 루트 뷰 컨트롤러로 스왑하려고 하는데... 전환 애니메이션을 추가하려고 합니다.

저는 이것을 하는 앱을 가지고 있습니다: 그것은 애니메이션으로 루트 뷰 컨트롤러를 변경합니다(알부멘이라고 함).

하지만 제 앱은 루트 뷰 컨트롤러를 실제로 변경하지는 않습니다.루트 보기 컨트롤러는 변경되지 않는 사용자 지정 컨테이너 보기 컨트롤러입니다.보기가 표시되지 않으며 기능도 없습니다.이것의 유일한 일은 변화가 일어나는 장소입니다. 즉, 한 하위 보기 컨트롤러를 다른 하위 보기 컨트롤러로 교체하여 전환 애니메이션이 작동합니다.

즉, 계층 맨 위에 있는 보기 컨트롤러 계층에 하나의 보기 컨트롤러를 추가하면 전체 문제가 깔끔하고 올바르게 해결됩니다.

대안 솔루션:

let stb = UIStoryboard(name: "YOUR_STORYBOARD_NAME", bundle: nil)
let rootVC = stb.instantiateViewController(withIdentifier: "YOUR_TABBAR_VIEWCONTROLLER_NAME")
let snapshot = (UIApplication.shared.keyWindow?.snapshotView(afterScreenUpdates: true))!
rootVC.view.addSubview(snapshot)

UIApplication.shared.keyWindow?.rootViewController = rootVC
UIView.transition(with: snapshot, 
                  duration: 0.4,
                  options: .transitionCrossDissolve,
                  animations: { 
                      snapshot.layer.opacity = 0
                  },
                  completion: { status in 
                      snapshot.removeFromSuperview()
                  })

사용해 보십시오.

UIView.transition(from: appdelegate.window.rootViewController!.view, to: tabbar.view, duration: 0.6, options: [.transitionCrossDissolve], completion: {
    _ in
    appdelegate.window.rootViewController = tabbar
})

업데이트된 Swift 5.3 버전:

    let foregroundedScenes = UIApplication.shared.connectedScenes.filter { $0.activationState == .foregroundActive }
    let window = foregroundedScenes.map { $0 as? UIWindowScene }.compactMap { $0 }.first?.windows.filter { $0.isKeyWindow }.first
    
    guard let uWindow = window else { return }

    uWindow.rootViewController = customTabBarController
    UIView.transition(with: uWindow, duration: 0.3, options: [.transitionCrossDissolve], animations: {}, completion: nil)
}

다음은 예입니다.transitionCrossDissolve와 함께transform translation YsnapshotView의 경우 일반 전환 애니메이션보다 더 좋아 보입니다.

테스트 대상Swift 4~5, iOS 11 ~ 15.7

if let window = UIApplication.shared.keyWindow {
    
    var snapShot = UIView()
    
    let destinationVC = UIViewController()
    if let realSnapShot = window.snapshotView(afterScreenUpdates: true) {
        snapShot = realSnapShot
    }
    destinationVC.view.addSubview(snapShot)
    window.rootViewController = destinationVC
    window.makeKeyAndVisible()
    
    UIView.transition(
        with: window,
        duration: 0.5,
        options: .transitionCrossDissolve,
        animations: {
            snapShot.transform = CGAffineTransform(translationX: 0, y: snapShot.frame.height)
        },
        completion: { status in
            snapShot.removeFromSuperview()
        }
    )
}

d.felber의 답변을 바탕으로 이에 대한 도우미 클래스를 만들었습니다.


    import UIKit

    class ViewPresenter {

        public static func replaceRootView(for viewController: UIViewController,
                                   duration: TimeInterval = 0.3,
                                   options: UIView.AnimationOptions = .transitionCrossDissolve,
                                   completion: ((Bool) -> Void)? = nil) {
            guard let window = UIApplication.shared.keyWindow else {
                return
            }

            guard let rootViewController = window.rootViewController else {
                return
            }

            viewController.view.frame = rootViewController.view.frame
            viewController.view.layoutIfNeeded()

            UIView.transition(with: window, duration: duration, options: options, animations: {
                window.rootViewController = viewController
            }, completion: completion)
        }
    }

다음과 같이 사용할 수 있습니다.

    let loginVC = SignInViewController(nibName: "SignInViewController", bundle: nil)
    ViewPresenter.replaceRootView(for: loginVC)

또는

ViewPresenter.replaceRootView(for: loginVC, duration: 0.3, options: .transitionCrossDissolve) { 
(bool) in
       // do something
}

언급URL : https://stackoverflow.com/questions/41144523/swap-rootviewcontroller-with-animation

반응형