ProHUD/Source/Guard/GuardController.swift

390 lines
12 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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