This PR contains all the work related to setting up this project as required to implement the [Assignment](https://repo.rock-n-code.com/rock-n-code/deep-linking-assignment/wiki/Assignment) on top, as intended. To summarise this work: - [x] created a new **Xcode** project; - [x] cloned the `Wikipedia` app and inserted it into the **Xcode** project; - [x] created the `Locations` app and also, its `Libraries` package; - [x] created the `Shared` package to share dependencies between the apps; - [x] added a `Makefile` file and implemented some **environment** and **help** commands. Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Reviewed-on: rock-n-code/deep-linking-assignment#1
107 lines
4.1 KiB
Swift
107 lines
4.1 KiB
Swift
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)
|
|
}
|
|
}
|