ProHUD/Source/Guard/GuardController.swift

387 lines
12 KiB
Swift
Raw Normal View History

2019-07-31 17:40:39 +08:00
//
// GuardController.swift
// ProHUD
//
// Created by xaoxuu on 2019/7/31.
// Copyright © 2019 Titan Studio. All rights reserved.
//
2020-06-10 13:08:34 +08:00
import UIKit
2019-08-03 16:22:50 +08:00
import SnapKit
2019-08-08 09:53:54 +08:00
public typealias Guard = ProHUD.Guard
2019-08-03 16:22:50 +08:00
public extension ProHUD {
2019-08-05 11:18:25 +08:00
2019-08-03 16:22:50 +08:00
class Guard: HUDController {
2019-08-05 11:18:25 +08:00
///
2019-08-12 15:02:36 +08:00
public var contentView = createBlurView()
2019-08-03 16:22:50 +08:00
2019-08-05 11:18:25 +08:00
/// textStackactionStack)
2019-08-03 16:22:50 +08:00
public var contentStack: StackContainer = {
let stack = StackContainer()
2019-08-03 17:48:37 +08:00
stack.spacing = cfg.guard.padding + cfg.guard.margin
2019-08-03 16:22:50 +08:00
stack.alignment = .fill
return stack
}()
2019-08-05 11:18:25 +08:00
///
2019-08-03 16:22:50 +08:00
public var textStack: StackContainer = {
let stack = StackContainer()
2019-08-03 17:48:37 +08:00
stack.spacing = cfg.guard.margin
2019-08-03 16:22:50 +08:00
stack.alignment = .fill
return stack
}()
2019-08-05 11:18:25 +08:00
///
2019-08-03 16:22:50 +08:00
public var actionStack: StackContainer = {
let stack = StackContainer()
stack.alignment = .fill
2019-08-03 17:48:37 +08:00
stack.spacing = cfg.guard.margin
2019-08-03 16:22:50 +08:00
return stack
}()
///
2019-08-12 19:02:33 +08:00
public var isForce = false
2019-08-03 16:22:50 +08:00
2019-08-12 17:59:40 +08:00
///
public var isFullScreen = false
2019-08-05 19:14:25 +08:00
///
2019-08-13 09:14:30 +08:00
private var isDisplaying = false
2019-08-05 11:18:25 +08:00
2019-08-08 09:25:28 +08:00
///
2019-08-12 17:59:40 +08:00
public var backgroundColor: UIColor? = UIColor(white: 0, alpha: 0.4)
2019-08-08 09:25:28 +08:00
2019-08-09 18:02:41 +08:00
public var vm = ViewModel()
2019-08-03 16:22:50 +08:00
// MARK:
2019-08-13 09:14:30 +08:00
private var isLoadFinished = false
2019-08-03 16:22:50 +08:00
///
/// - Parameter title:
/// - Parameter message:
2019-08-08 09:53:54 +08:00
/// - Parameter actions:
2019-08-12 20:20:07 +08:00
public convenience init(title: String? = nil, message: String? = nil, actions: ((Guard) -> Void)? = nil) {
2019-08-03 16:22:50 +08:00
self.init()
2019-08-09 18:02:41 +08:00
vm.vc = self
2019-08-03 16:22:50 +08:00
if let _ = title {
2019-08-05 14:20:52 +08:00
add(title: title)
2019-08-03 16:22:50 +08:00
}
if let _ = message {
2019-08-05 14:20:52 +08:00
add(message: message)
2019-08-03 16:22:50 +08:00
}
2019-08-12 20:20:07 +08:00
actions?(self)
2019-08-09 18:02:41 +08:00
2019-08-08 09:53:54 +08:00
//
2019-08-03 16:22:50 +08:00
let tap = UITapGestureRecognizer(target: self, action: #selector(privDidTapped(_:)))
view.addGestureRecognizer(tap)
}
2019-08-12 15:02:36 +08:00
public override func viewDidLoad() {
super.viewDidLoad()
view.tintColor = cfg.guard.tintColor
cfg.guard.reloadData(self)
isLoadFinished = true
}
2019-08-05 11:18:25 +08:00
}
2019-08-08 09:53:54 +08:00
2019-08-05 11:18:25 +08:00
}
// MARK: -
2019-08-08 09:53:54 +08:00
public extension Guard {
2019-08-05 11:18:25 +08:00
///
/// - Parameter viewController:
2019-08-09 18:02:41 +08:00
@discardableResult func push(to viewController: UIViewController? = nil) -> Guard {
2020-06-19 10:48:47 +08:00
func f(_ vc: UIViewController) -> Guard {
2019-08-05 11:18:25 +08:00
view.layoutIfNeeded()
vc.addChild(self)
vc.view.addSubview(view)
view.isUserInteractionEnabled = true
view.snp.makeConstraints { (mk) in
mk.edges.equalToSuperview()
}
2019-08-13 09:14:30 +08:00
if isDisplaying == false {
2019-08-12 15:02:36 +08:00
privTranslateOut()
2019-08-05 11:18:25 +08:00
}
2019-08-13 09:14:30 +08:00
isDisplaying = true
2019-08-05 11:18:25 +08:00
UIView.animateForGuard {
2019-08-12 15:02:36 +08:00
self.privTranslateIn()
2019-08-03 16:22:50 +08:00
}
2020-06-19 10:48:47 +08:00
return self
2019-08-03 16:22:50 +08:00
}
2019-08-08 09:25:28 +08:00
if let vc = viewController ?? cfg.rootViewController {
2020-06-19 10:48:47 +08:00
return f(vc)
2019-08-08 09:25:28 +08:00
} else {
2020-06-19 10:48:47 +08:00
// RootVC
let ws = UIApplication.shared.windows.filter { (w) -> Bool in
2020-06-19 10:48:47 +08:00
// UITextEffectsWindow Window
if "\(type(of:w))" == "UIWindow" && w.isHidden == false && w.windowLevel == .normal {
2020-06-19 10:48:47 +08:00
return true
} else {
return false
}
}
for w in ws {
if let vc = w.rootViewController {
return f(vc)
}
}
2020-06-19 10:54:55 +08:00
debug("自动获取根控制器失败请设置根控制器或者传入需要push到的控制器")
2019-08-08 09:25:28 +08:00
}
2019-08-08 11:09:22 +08:00
return self
2019-08-05 11:18:25 +08:00
}
///
2019-08-08 11:09:22 +08:00
func pop() {
2019-08-13 09:14:30 +08:00
if isDisplaying {
2019-08-05 11:18:25 +08:00
debug("pop")
2019-08-12 19:02:33 +08:00
willDisappearCallback?()
2019-08-13 09:14:30 +08:00
isDisplaying = false
2019-08-05 11:18:25 +08:00
view.isUserInteractionEnabled = false
self.removeFromParent()
2019-08-08 11:09:22 +08:00
UIView.animateForGuard(animations: {
2019-08-12 15:02:36 +08:00
self.privTranslateOut()
2019-08-08 11:09:22 +08:00
}) { (done) in
2019-08-13 09:14:30 +08:00
if self.isDisplaying == false {
2019-08-05 11:18:25 +08:00
self.view.removeFromSuperview()
2019-08-03 16:22:50 +08:00
}
}
}
}
2019-08-12 15:02:36 +08:00
///
/// - Parameter callback:
func update(_ callback: ((inout ViewModel) -> Void)? = nil) {
callback?(&vm)
cfg.guard.reloadData(self)
}
}
// MARK: -
public extension Guard {
///
/// - Parameter alert:
/// - Parameter title:
/// - Parameter message:
/// - Parameter icon:
2019-08-12 20:20:07 +08:00
@discardableResult class func push(to viewController: UIViewController? = nil, _ actions: ((Guard) -> Void)? = nil) -> Guard {
2019-08-12 15:02:36 +08:00
return Guard(actions: actions).push(to: viewController)
}
2019-08-13 09:14:30 +08:00
///
2019-08-12 15:02:36 +08:00
/// - Parameter identifier:
2019-08-12 20:20:07 +08:00
class func find(_ identifier: String?, from viewController: UIViewController? = nil) -> [Guard] {
2019-08-12 15:02:36 +08:00
var gg = [Guard]()
if let vc = viewController ?? cfg.rootViewController {
for child in vc.children {
if child.isKind(of: Guard.self) {
if let g = child as? Guard {
if let id = identifier {
2019-08-12 20:20:07 +08:00
if g.identifier == id {
2019-08-12 15:02:36 +08:00
gg.append(g)
}
} else {
gg.append(g)
}
}
}
}
}
return gg
}
2019-08-12 20:20:07 +08:00
///
/// - Parameter identifier:
/// - Parameter last:
/// - Parameter none:
class func find(_ identifier: String?, from viewController: UIViewController? = nil, last: ((Guard) -> Void)? = nil, none: (() -> Void)? = nil) {
if let t = find(identifier, from: viewController).last {
last?(t)
} else {
none?()
}
}
2019-08-12 15:02:36 +08:00
///
/// - Parameter alert:
class func pop(_ guard: Guard) {
`guard`.pop()
}
///
/// - Parameter identifier:
2019-08-12 20:20:07 +08:00
class func pop(_ identifier: String?, from viewController: UIViewController?) {
for g in find(identifier, from: viewController) {
2019-08-12 15:02:36 +08:00
g.pop()
}
}
}
// MARK: -
internal extension Guard {
2019-08-08 11:09:22 +08:00
2019-08-05 11:18:25 +08:00
///
/// - Parameter text:
2019-08-05 14:20:52 +08:00
@discardableResult func add(title: String?) -> UILabel {
2019-08-03 17:48:37 +08:00
let lb = UILabel()
lb.font = cfg.guard.titleFont
lb.textColor = cfg.primaryLabelColor
lb.numberOfLines = 0
2019-08-12 15:02:36 +08:00
lb.textAlignment = .center
2019-08-05 14:20:52 +08:00
lb.text = title
2019-08-03 17:48:37 +08:00
textStack.addArrangedSubview(lb)
if #available(iOS 11.0, *) {
let count = textStack.arrangedSubviews.count
if count > 1 {
2020-06-15 15:11:44 +08:00
textStack.setCustomSpacing(cfg.guard.margin * 3, after: textStack.arrangedSubviews[count-2])
textStack.setCustomSpacing(cfg.guard.margin * 1.5, after: textStack.arrangedSubviews[count-1])
2019-08-03 17:48:37 +08:00
}
2019-08-03 16:22:50 +08:00
} else {
2019-08-03 17:48:37 +08:00
// Fallback on earlier versions
2019-08-03 16:22:50 +08:00
}
2019-08-08 09:25:28 +08:00
cfg.guard.reloadStack(self)
return lb
}
///
/// - Parameter text:
@discardableResult func add(subTitle: String?) -> UILabel {
let lb = add(title: subTitle)
lb.font = cfg.guard.subTitleFont
2019-08-12 15:02:36 +08:00
lb.textAlignment = .justified
2019-08-03 16:22:50 +08:00
return lb
}
2019-08-05 11:18:25 +08:00
///
/// - Parameter text:
2019-08-05 14:20:52 +08:00
@discardableResult func add(message: String?) -> UILabel {
2019-08-03 17:48:37 +08:00
let lb = UILabel()
lb.font = cfg.guard.bodyFont
lb.textColor = cfg.secondaryLabelColor
lb.numberOfLines = 0
lb.textAlignment = .justified
2019-08-05 14:20:52 +08:00
lb.text = message
2019-08-03 16:22:50 +08:00
textStack.addArrangedSubview(lb)
2019-08-08 09:25:28 +08:00
cfg.guard.reloadStack(self)
2019-08-03 16:22:50 +08:00
return lb
}
2019-08-12 20:20:07 +08:00
///
2019-08-12 15:02:36 +08:00
/// - Parameter index:
2019-08-05 11:18:25 +08:00
/// - Parameter style:
/// - Parameter title:
/// - Parameter action:
2019-08-12 15:02:36 +08:00
@discardableResult func insert(action index: Int?, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) -> UIButton {
2019-08-03 16:22:50 +08:00
let btn = Button.actionButton(title: title)
2019-08-03 17:48:37 +08:00
btn.titleLabel?.font = cfg.guard.buttonFont
2019-08-12 15:02:36 +08:00
if let idx = index, idx < actionStack.arrangedSubviews.count {
actionStack.insertArrangedSubview(btn, at: idx)
} else {
actionStack.addArrangedSubview(btn)
}
2019-08-03 16:22:50 +08:00
btn.update(style: style)
2019-08-12 15:02:36 +08:00
cfg.guard.reloadStack(self)
2019-08-03 16:22:50 +08:00
addTouchUpAction(for: btn) { [weak self] in
2019-08-08 09:53:54 +08:00
handler?()
2019-08-03 16:22:50 +08:00
if btn.tag == UIAlertAction.Style.cancel.rawValue {
self?.pop()
}
}
2019-08-12 15:02:36 +08:00
if isLoadFinished {
UIView.animateForGuard {
self.view.layoutIfNeeded()
}
}
2019-08-03 16:22:50 +08:00
return btn
}
2019-08-12 20:20:07 +08:00
///
/// - Parameter index:
/// - Parameter style:
/// - Parameter title:
/// - Parameter handler:
2019-08-12 15:02:36 +08:00
func update(action index: Int, style: UIAlertAction.Style, title: String?, handler: (() -> Void)?) {
if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
btn.setTitle(title, for: .normal)
if let b = btn as? Button {
b.update(style: style)
}
if let _ = buttonEvents[btn] {
buttonEvents.removeValue(forKey: btn)
}
addTouchUpAction(for: btn) { [weak self] in
handler?()
if btn.tag == UIAlertAction.Style.cancel.rawValue {
self?.pop()
}
}
2019-08-08 09:25:28 +08:00
}
}
2019-08-03 16:22:50 +08:00
2019-08-12 15:02:36 +08:00
///
/// - Parameter index:
@discardableResult func remove(index: Int) -> Guard {
if index < 0 {
for view in self.actionStack.arrangedSubviews {
if let btn = view as? UIButton {
btn.removeFromSuperview()
2019-08-12 17:59:40 +08:00
if let _ = buttonEvents[btn] {
buttonEvents.removeValue(forKey: btn)
}
2019-08-08 09:25:28 +08:00
}
}
2019-08-12 15:02:36 +08:00
} else if index < self.actionStack.arrangedSubviews.count, let btn = self.actionStack.arrangedSubviews[index] as? UIButton {
btn.removeFromSuperview()
2019-08-12 17:59:40 +08:00
if let _ = buttonEvents[btn] {
buttonEvents.removeValue(forKey: btn)
}
2019-08-08 09:25:28 +08:00
}
2019-08-12 15:02:36 +08:00
cfg.guard.reloadStack(self)
UIView.animateForAlert {
self.view.layoutIfNeeded()
2019-08-08 11:09:22 +08:00
}
2019-08-12 15:02:36 +08:00
return self
2019-08-08 11:09:22 +08:00
}
2019-08-05 19:14:25 +08:00
}
2019-08-12 15:02:36 +08:00
fileprivate extension Guard {
2019-08-03 16:22:50 +08:00
///
/// - Parameter sender:
@objc func privDidTapped(_ sender: UITapGestureRecognizer) {
let point = sender.location(in: contentView)
if point.x < 0 || point.y < 0 {
2019-08-12 19:02:33 +08:00
if isForce == false {
2019-08-03 16:22:50 +08:00
//
pop()
}
}
}
2019-08-12 15:02:36 +08:00
func privTranslateIn() {
2019-08-08 09:25:28 +08:00
view.backgroundColor = backgroundColor
2019-08-03 16:22:50 +08:00
contentView.transform = .identity
}
2019-08-12 15:02:36 +08:00
func privTranslateOut() {
2019-08-03 16:22:50 +08:00
view.backgroundColor = UIColor(white: 0, alpha: 0)
2019-08-03 17:48:37 +08:00
contentView.transform = .init(translationX: 0, y: view.frame.size.height - contentView.frame.minY + cfg.guard.margin)
2019-08-03 16:22:50 +08:00
}
}