/
SplashScreen.swift
154 lines (129 loc) · 6 KB
/
SplashScreen.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import Foundation
import Capacitor
@objc public class SplashScreen: NSObject {
var parentView: UIView
var viewController = UIViewController()
var spinner = UIActivityIndicatorView()
var config: SplashScreenConfig = SplashScreenConfig()
var hideTask: Any?
var isVisible: Bool = false
init(parentView: UIView, config: SplashScreenConfig) {
self.parentView = parentView
self.config = config
}
public func showOnLaunch() {
buildViews()
if self.config.launchShowDuration == 0 {
return
}
var settings = SplashScreenSettings()
settings.showDuration = config.launchShowDuration
settings.fadeInDuration = config.launchFadeInDuration
settings.autoHide = config.launchAutoHide
showSplash(settings: settings, completion: {}, isLaunchSplash: true)
}
public func show(settings: SplashScreenSettings, completion: @escaping () -> Void) {
self.showSplash(settings: settings, completion: completion, isLaunchSplash: false)
}
public func hide(settings: SplashScreenSettings) {
hideSplash(fadeOutDuration: settings.fadeOutDuration, isLaunchSplash: false)
}
private func showSplash(settings: SplashScreenSettings, completion: @escaping () -> Void, isLaunchSplash: Bool) {
DispatchQueue.main.async { [weak self] in
guard let strongSelf = self else {
return
}
if let backgroundColor = strongSelf.config.backgroundColor {
strongSelf.viewController.view.backgroundColor = backgroundColor
}
if strongSelf.config.showSpinner {
if let style = strongSelf.config.spinnerStyle {
strongSelf.spinner.style = style
}
if let spinnerColor = strongSelf.config.spinnerColor {
strongSelf.spinner.color = spinnerColor
}
}
strongSelf.parentView.addSubview(strongSelf.viewController.view)
if strongSelf.config.showSpinner {
strongSelf.parentView.addSubview(strongSelf.spinner)
strongSelf.spinner.centerXAnchor.constraint(equalTo: strongSelf.parentView.centerXAnchor).isActive = true
strongSelf.spinner.centerYAnchor.constraint(equalTo: strongSelf.parentView.centerYAnchor).isActive = true
}
strongSelf.parentView.isUserInteractionEnabled = false
UIView.transition(with: strongSelf.viewController.view, duration: TimeInterval(Double(settings.fadeInDuration) / 1000), options: .curveLinear, animations: {
strongSelf.viewController.view.alpha = 1
if strongSelf.config.showSpinner {
strongSelf.spinner.alpha = 1
}
}) { (_: Bool) in
strongSelf.isVisible = true
if settings.autoHide {
strongSelf.hideTask = DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + (Double(settings.showDuration) / 1000)
) {
strongSelf.hideSplash(fadeOutDuration: settings.fadeOutDuration, isLaunchSplash: isLaunchSplash)
completion()
}
} else {
completion()
}
}
}
}
private func buildViews() {
if let vc = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController() {
viewController = vc
}
// Observe for changes on frame and bounds to handle rotation resizing
parentView.addObserver(self, forKeyPath: "frame", options: .new, context: nil)
parentView.addObserver(self, forKeyPath: "bounds", options: .new, context: nil)
updateSplashImageBounds()
if config.showSpinner {
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.startAnimating()
}
}
private func tearDown() {
isVisible = false
parentView.isUserInteractionEnabled = true
viewController.view.removeFromSuperview()
if config.showSpinner {
spinner.removeFromSuperview()
}
}
// Update the bounds for the splash image. This will also be called when
// the parent view observers fire
private func updateSplashImageBounds() {
guard let delegate = UIApplication.shared.delegate else {
CAPLog.print("Unable to find root window object for SplashScreen bounds. Please file an issue")
return
}
guard let window = delegate.window as? UIWindow else {
CAPLog.print("Unable to find root window object for SplashScreen bounds. Please file an issue")
return
}
viewController.view.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: window.bounds.size)
}
override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change _: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
updateSplashImageBounds()
}
private func hideSplash(fadeOutDuration: Int, isLaunchSplash: Bool) {
if isLaunchSplash, isVisible {
CAPLog.print("SplashScreen.hideSplash: SplashScreen was automatically hidden after default timeout. " +
"You should call `SplashScreen.hide()` as soon as your web app is loaded (or increase the timeout). " +
"Read more at https://capacitorjs.com/docs/apis/splash-screen#hiding-the-splash-screen")
}
if !isVisible { return }
DispatchQueue.main.async {
UIView.transition(with: self.viewController.view, duration: TimeInterval(Double(fadeOutDuration) / 1000), options: .curveLinear, animations: {
self.viewController.view.alpha = 0
if self.config.showSpinner {
self.spinner.alpha = 0
}
}) { (_: Bool) in
self.tearDown()
}
}
}
}