Signal-iOS/Signal/src/ViewControllers/LoadingViewController.swift
2021-09-03 11:41:34 -07:00

168 lines
5.7 KiB
Swift

//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
//
import Foundation
// The initial presentation is intended to be indistinguishable from the Launch Screen.
// After a delay we present some "loading" UI so the user doesn't think the app is frozen.
@objc
public class LoadingViewController: UIViewController {
var logoView: UIImageView!
var topLabel: UILabel!
var bottomLabel: UILabel!
let labelStack = UIStackView()
var topLabelTimer: Timer?
var bottomLabelTimer: Timer?
override public func loadView() {
self.view = UIView()
view.backgroundColor = Theme.launchScreenBackground
self.logoView = UIImageView(image: #imageLiteral(resourceName: "signal-logo-128-launch-screen"))
view.addSubview(logoView)
logoView.autoCenterInSuperview()
logoView.autoSetDimensions(to: CGSize(square: 128))
self.topLabel = buildLabel()
topLabel.alpha = 0
topLabel.font = UIFont.ows_dynamicTypeTitle2
topLabel.text = NSLocalizedString("DATABASE_VIEW_OVERLAY_TITLE", comment: "Title shown while the app is updating its database.")
labelStack.addArrangedSubview(topLabel)
self.bottomLabel = buildLabel()
bottomLabel.alpha = 0
bottomLabel.font = UIFont.ows_dynamicTypeBody
bottomLabel.text = NSLocalizedString("DATABASE_VIEW_OVERLAY_SUBTITLE", comment: "Subtitle shown while the app is updating its database.")
labelStack.addArrangedSubview(bottomLabel)
labelStack.axis = .vertical
labelStack.alignment = .center
labelStack.spacing = 8
view.addSubview(labelStack)
labelStack.autoPinEdge(.top, to: .bottom, of: logoView, withOffset: 20)
labelStack.autoPinLeadingToSuperviewMargin()
labelStack.autoPinTrailingToSuperviewMargin()
labelStack.setCompressionResistanceHigh()
labelStack.setContentHuggingHigh()
NotificationCenter.default.addObserver(self,
selector: #selector(didBecomeActive),
name: .OWSApplicationDidBecomeActive,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(didEnterBackground),
name: .OWSApplicationDidEnterBackground,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(themeDidChange),
name: .ThemeDidChange,
object: nil)
}
override public func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// We only show the "loading" UI if it's a slow launch. Otherwise this ViewController
// should be indistinguishable from the launch screen.
let kTopLabelThreshold: TimeInterval = 5
topLabelTimer = Timer.weakScheduledTimer(withTimeInterval: kTopLabelThreshold, target: self, selector: #selector(showTopLabel), userInfo: nil, repeats: false)
let kBottomLabelThreshold: TimeInterval = 15
topLabelTimer = Timer.weakScheduledTimer(withTimeInterval: kBottomLabelThreshold, target: self, selector: #selector(showBottomLabelAnimated), userInfo: nil, repeats: false)
}
// UIStackView removes hidden subviews from the layout.
// UIStackView considers views with a sufficiently low
// alpha to be "hidden". This can cause layout to glitch
// briefly when returning from background. Therefore we
// use a "min" alpha value when fading in labels that is
// high enough to avoid this UIStackView behavior.
private let kMinAlpha: CGFloat = 0.1
@objc
private func showBottomLabelAnimated() {
Logger.verbose("")
bottomLabel.layer.removeAllAnimations()
bottomLabel.alpha = kMinAlpha
UIView.animate(withDuration: 0.1) {
self.bottomLabel.alpha = 1
}
}
@objc
private func showTopLabel() {
topLabel.layer.removeAllAnimations()
topLabel.alpha = 0.2
UIView.animate(withDuration: 0.9, delay: 0, options: [.autoreverse, .repeat, .curveEaseInOut], animations: {
self.topLabel.alpha = 1.0
}, completion: nil)
}
private func showBottomLabel() {
bottomLabel.layer.removeAllAnimations()
self.bottomLabel.alpha = 1
}
// MARK: -
@objc func didBecomeActive() {
AssertIsOnMainThread()
Logger.info("")
guard viewHasEnteredBackground else {
// If the app is returning from background, skip any
// animations and show the top and bottom labels.
return
}
topLabelTimer?.invalidate()
topLabelTimer = nil
bottomLabelTimer?.invalidate()
bottomLabelTimer = nil
showTopLabel()
showBottomLabel()
labelStack.layoutSubviews()
view.layoutSubviews()
}
private var viewHasEnteredBackground = false
@objc func didEnterBackground() {
AssertIsOnMainThread()
Logger.info("")
viewHasEnteredBackground = true
}
@objc func themeDidChange() {
view.backgroundColor = Theme.launchScreenBackground
}
// MARK: Orientation
override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIDevice.current.isIPad ? .all : .portrait
}
// MARK:
private func buildLabel() -> UILabel {
let label = UILabel()
label.textColor = .white
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
return label
}
}