deep-linking-sample/Apps/Wikipedia/Wikipedia/Code/SplashScreenViewController.swift

107 lines
4.1 KiB
Swift
Raw Normal View History

import UIKit
/// Matches the appearance of the launch xib and shows while we do any setup or migrations that need to block user interaction.
/// If this VC is shown for longer than `maximumNonInteractiveTimeInterval`, the view transitions to a loading animation.
@objc (WMFSplashScreenViewController)
class SplashScreenViewController: ThemeableViewController {
// MARK: Object Lifecycle
deinit {
// Should be canceled on willDisappear, but this is extra insurance
NSObject.cancelPreviousPerformRequests(withTarget: self)
}
// MARK: View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupSplashView()
}
func triggerMigratingAnimation() {
perform(#selector(showLoadingAnimation), with: nil, afterDelay: SplashScreenViewController.maximumNonInteractiveTimeInterval)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(showLoadingAnimation), object: nil)
}
/// Ensure we show the busy animation for some minimum amount of time, otherwise the transition can be jarring
@objc func ensureMinimumShowDuration(completion: @escaping () -> Void) {
guard loadingAnimationShowTime != 0 else {
completion()
return
}
let now = CFAbsoluteTimeGetCurrent()
let busyAnimationVisibleTimeInterval = now - loadingAnimationShowTime
guard busyAnimationVisibleTimeInterval < SplashScreenViewController.minimumBusyAnimationVisibleTimeInterval else {
completion()
return
}
let delay = SplashScreenViewController.minimumBusyAnimationVisibleTimeInterval - busyAnimationVisibleTimeInterval
dispatchOnMainQueueAfterDelayInSeconds(delay, completion)
}
// MARK: Constants
static let maximumNonInteractiveTimeInterval: TimeInterval = 4
static let minimumBusyAnimationVisibleTimeInterval: TimeInterval = 0.6
static let crossFadeAnimationDuration: TimeInterval = 0.3
// MARK: Splash View
func setupSplashView() {
view.wmf_addSubviewWithConstraintsToEdges(splashView)
let wordmark = UIImage(named: "splashscreen-wordmark")
let wordmarkView = UIImageView(image: wordmark)
wordmarkView.translatesAutoresizingMaskIntoConstraints = false
splashView.addSubview(wordmarkView)
let centerXConstraint = splashView.centerXAnchor.constraint(equalTo: wordmarkView.centerXAnchor)
let centerYConstraint = splashView.centerYAnchor.constraint(equalTo: wordmarkView.centerYAnchor, constant: 12)
splashView.addConstraints([centerXConstraint, centerYConstraint])
}
/// Matches launch xib
lazy var splashView: UIImageView = {
let splashView = UIImageView()
splashView.translatesAutoresizingMaskIntoConstraints = false
splashView.contentMode = .center
if UIDevice.current.userInterfaceIdiom != .pad {
splashView.image = UIImage(named: "splashscreen-background")
}
splashView.backgroundColor = UIColor.systemBackground
return splashView
}()
// MARK: Loading Animation
var loadingAnimationShowTime: CFAbsoluteTime = 0
lazy var loadingAnimationViewController: LoadingAnimationViewController = {
return LoadingAnimationViewController(nibName: "LoadingAnimationViewController", bundle: nil)
}()
@objc private func showLoadingAnimation() {
loadingAnimationShowTime = CFAbsoluteTimeGetCurrent()
wmf_add(childController: loadingAnimationViewController, andConstrainToEdgesOfContainerView: view, belowSubview: splashView)
UIView.animate(withDuration: SplashScreenViewController.crossFadeAnimationDuration) {
self.splashView.alpha = 0
}
}
// MARK: Themable
override func apply(theme: Theme) {
super.apply(theme: theme)
guard viewIfLoaded != nil else {
return
}
loadingAnimationViewController.apply(theme: theme)
}
}