ProHUD/Source/Guard/GuardController.swift

373 lines
11 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

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
import Inspire
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) {
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()
}
}
if let vc = viewController ?? cfg.rootViewController {
f(vc)
} else {
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()
}
}
}
}
///
/// - 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.actionButton(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)
}
}