管理视图层次结构有很多正确的方法,但我会分享一种我发现简单而有效的方式。
基本上,我在注销/登录时交换主 UIWindow
的 rootViewController
。此外,我编程提供 rootViewController
,而不是让 @UIApplicationMain
加载初始视图控制器。这样做的好处是,在应用程序启动期间,如果用户已经登录,则不需要加载 Login.storyboard
。
show
函数可以根据您的风格进行配置,但我喜欢交叉溶解转换,因为它们非常简单。
![enter image description here](https://istack.dev59.com/HqxfS.webp)
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
lazy var window: UIWindow? = {
let window = UIWindow()
window.makeKeyAndVisible()
return window
}()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let isLoggedIn = false
if isLoggedIn {
show(MainViewController(), animated: false)
} else {
show(LoginViewController(), animated: false)
}
return true
}
}
class LoginViewController: UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .red
let logoutButton = UIButton()
logoutButton.setTitle("Log In", for: .normal)
logoutButton.addTarget(self, action: #selector(login), for: .touchUpInside)
view.addSubview(logoutButton)
logoutButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(
[logoutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
logoutButton.centerYAnchor.constraint(equalTo: view.centerYAnchor)]
)
self.view = view
}
@objc
func login() {
AppDelegate.shared.show(MainViewController())
}
}
class MainViewController: UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .blue
let logoutButton = UIButton()
logoutButton.setTitle("Log Out", for: .normal)
logoutButton.addTarget(self, action: #selector(logout), for: .touchUpInside)
view.addSubview(logoutButton)
logoutButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(
[logoutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
logoutButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
]
)
self.view = view
}
@objc
func logout() {
AppDelegate.shared.show(LoginViewController())
}
}
extension AppDelegate {
static var shared: AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
}
private let kTransitionSemaphore = DispatchSemaphore(value: 1)
extension AppDelegate {
func show(_ viewController: UIViewController,
animated: Bool = true,
options: UIViewAnimationOptions = [.transitionCrossDissolve, .curveEaseInOut],
completion: (() -> Void)? = nil) {
guard let window = window else { return }
if animated == false {
window.rootViewController = viewController
return
}
DispatchQueue.global(qos: .userInitiated).async {
kTransitionSemaphore.wait()
DispatchQueue.main.async {
let duration = 0.35
let previousAreAnimationsEnabled = UIView.areAnimationsEnabled
UIView.setAnimationsEnabled(false)
UIView.transition(with: window, duration: duration, options: options, animations: {
self.window?.rootViewController = viewController
}, completion: { _ in
UIView.setAnimationsEnabled(previousAreAnimationsEnabled)
kTransitionSemaphore.signal()
completion?()
})
}
}
}
}
这段代码是一个完整的示例,您可以创建一个新项目,清空“主界面”字段,然后将此代码放入应用程序委托中。
最终的过渡效果:
![enter image description here](https://istack.dev59.com/WeUBQ.gif)
LoginViewController
上保留mainPlatformRootControler
?如果从LoginViewController
呈现RootViewController
,则可以通过使用self.presentingViewController
从RootViewController
获取loginViewController
,然后调用一个方法而不使用通知。 - trungduc