mirror of https://github.com/xaoxuu/ProHUD
254 lines
8.3 KiB
Swift
254 lines
8.3 KiB
Swift
//
|
|
// HUDView.swift
|
|
// ProHUD
|
|
//
|
|
// Created by xaoxuu on 2019/7/29.
|
|
// Copyright © 2019 Titan Studio. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
// MARK: - public
|
|
|
|
public extension ProHUD {
|
|
|
|
/// 堆栈视图容器
|
|
class StackContainer: UIStackView {
|
|
|
|
public override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
spacing = cfg.alert.margin
|
|
distribution = .fill
|
|
alignment = .center
|
|
axis = .vertical
|
|
|
|
}
|
|
|
|
required init(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
}
|
|
|
|
/// 旋转方向
|
|
enum RotateDirection: Double {
|
|
/// 顺时针
|
|
case clockwise = 1
|
|
/// 逆时针
|
|
case counterclockwise = -1
|
|
}
|
|
|
|
|
|
/// 进度指示器
|
|
class ProgressView: UIView {
|
|
|
|
var progressLayer = CAShapeLayer()
|
|
|
|
override init(frame: CGRect) {
|
|
// 容器宽度
|
|
let maxSize = CGFloat(28)
|
|
super.init(frame: .init(x: 0, y: 0, width: maxSize, height: maxSize))
|
|
layer.cornerRadius = maxSize / 2
|
|
layer.masksToBounds = true
|
|
// 进度圆半径
|
|
let radius = maxSize / 2 - 4
|
|
backgroundColor = .white
|
|
|
|
let path = UIBezierPath(arcCenter: CGPoint(x: 14, y: 14), radius: radius/2, startAngle: -CGFloat.pi*0.5, endAngle: CGFloat.pi * 1.5, clockwise: true)
|
|
|
|
progressLayer.fillColor = UIColor.clear.cgColor
|
|
progressLayer.path = path.cgPath
|
|
|
|
progressLayer.strokeColor = tintColor.cgColor
|
|
progressLayer.lineWidth = radius
|
|
progressLayer.strokeStart = 0
|
|
progressLayer.strokeEnd = 0
|
|
layer.addSublayer(progressLayer)
|
|
|
|
}
|
|
|
|
required init(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
func updateProgress(progress: CGFloat) {
|
|
if progress <= 1 {
|
|
// 初始化
|
|
if progressLayer.superlayer == nil {
|
|
progressLayer.strokeEnd = 0
|
|
layer.addSublayer(progressLayer)
|
|
}
|
|
progressLayer.strokeEnd = progress
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - internal
|
|
|
|
// MARK: 弹窗
|
|
internal extension Alert {
|
|
|
|
/// 弹窗的按钮
|
|
class Button: UIButton {
|
|
|
|
/// 创建操作按钮
|
|
/// - Parameter title: 标题
|
|
/// - Returns: 按钮
|
|
static func createActionButton(title: String?) -> Button {
|
|
let btn = Button(type: .system)
|
|
btn.setTitle(title, for: .normal)
|
|
btn.layer.cornerRadius = cfg.alert.cornerRadius / 2
|
|
btn.titleLabel?.font = cfg.alert.buttonFont
|
|
return btn
|
|
}
|
|
|
|
/// 更新按钮
|
|
/// - Parameter style: 样式
|
|
func update(style: UIAlertAction.Style) {
|
|
let pd = CGFloat(8)
|
|
if style != .cancel {
|
|
backgroundColor = cfg.dynamicColor.withAlphaComponent(0.04)
|
|
contentEdgeInsets = .init(top: pd*1.5, left: pd*1.5, bottom: pd*1.5, right: pd*1.5)
|
|
} else {
|
|
backgroundColor = .clear
|
|
contentEdgeInsets = .init(top: pd*1, left: pd*1.5, bottom: pd*1, right: pd*1.5)
|
|
}
|
|
switch style {
|
|
case .default:
|
|
setTitleColor(tintColor, for: .normal)
|
|
case .destructive:
|
|
setTitleColor(.init(red: 244/255, green: 67/255, blue: 54/255, alpha: 1), for: .normal)
|
|
case .cancel:
|
|
setTitleColor(cfg.secondaryLabelColor, for: .normal)
|
|
@unknown default:
|
|
break
|
|
}
|
|
tag = style.rawValue
|
|
}
|
|
|
|
/// 创建隐藏按钮
|
|
/// - Returns: 按钮
|
|
static func createHideButton() -> UIButton {
|
|
let btn = Button(type: .system)
|
|
let pd = cfg.alert.padding/2
|
|
btn.contentEdgeInsets = .init(top: pd*1.5, left: pd*1.5, bottom: pd*1.5, right: pd*1.5)
|
|
btn.imageEdgeInsets.right = pd*1.5
|
|
btn.setTitleColor(UIColor(red:1.00, green:0.55, blue:0.21, alpha:1.00), for: .normal)
|
|
btn.titleLabel?.font = cfg.alert.buttonFont
|
|
return btn
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: 操作表
|
|
internal extension Guard {
|
|
|
|
/// 操作表的按钮
|
|
class Button: UIButton {
|
|
|
|
/// 创建操作按钮
|
|
/// - Parameter title: 标题
|
|
/// - Returns: 按钮
|
|
static func createActionButton(title: String?) -> Button {
|
|
let btn = Button(type: .system)
|
|
btn.setTitle(title, for: .normal)
|
|
btn.layer.cornerRadius = cfg.guard.buttonCornerRadius
|
|
btn.titleLabel?.font = cfg.guard.buttonFont
|
|
return btn
|
|
}
|
|
|
|
func update(style: UIAlertAction.Style) {
|
|
let pd = CGFloat(8)
|
|
if style != .cancel {
|
|
contentEdgeInsets = .init(top: pd*1.5+2, left: pd*1.5, bottom: pd*1.5+2, right: pd*1.5)
|
|
} else {
|
|
contentEdgeInsets = .init(top: pd*1+2, left: pd*1.5, bottom: pd*1+2, right: pd*1.5)
|
|
}
|
|
switch style {
|
|
case .default:
|
|
backgroundColor = tintColor
|
|
setTitleColor(.white, for: .normal)
|
|
case .destructive:
|
|
backgroundColor = .init(red: 244/255, green: 67/255, blue: 54/255, alpha: 1)
|
|
setTitleColor(.white, for: .normal)
|
|
case .cancel:
|
|
backgroundColor = .clear
|
|
setTitleColor(cfg.secondaryLabelColor, for: .normal)
|
|
@unknown default:
|
|
break
|
|
}
|
|
tag = style.rawValue
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// MARK: - 动画
|
|
|
|
internal extension UIView {
|
|
|
|
private class func animateEaseOut(duration: TimeInterval = 1, delay: TimeInterval = 0, animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
|
animate(withDuration: duration, delay: delay, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.allowUserInteraction, .curveEaseOut], animations: animations, completion: completion)
|
|
}
|
|
|
|
class func animateForAlertBuildIn(animations: @escaping () -> Void) {
|
|
animateEaseOut(duration: 0.8, delay: 0, animations: animations, completion: nil)
|
|
}
|
|
class func animateForAlertBuildOut(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
|
animateEaseOut(duration: 0.38, delay: 0, animations: animations, completion: completion)
|
|
}
|
|
|
|
class func animateForAlert(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
|
animateEaseOut(duration: 1, delay: 0, animations: animations, completion: completion)
|
|
}
|
|
class func animateForAlert(animations: @escaping () -> Void) {
|
|
animateForAlert(animations: animations, completion: nil)
|
|
}
|
|
|
|
class func animateForToast(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
|
animateEaseOut(duration: 1.2, delay: 0, animations: animations, completion: completion)
|
|
}
|
|
class func animateForToast(animations: @escaping () -> Void) {
|
|
animateForToast(animations: animations, completion: nil)
|
|
}
|
|
|
|
class func animateForGuard(animations: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) {
|
|
animateEaseOut(duration: 0.45, delay: 0, animations: animations, completion: completion)
|
|
}
|
|
class func animateForGuard(animations: @escaping () -> Void) {
|
|
animateForGuard(animations: animations, completion: nil)
|
|
}
|
|
|
|
}
|
|
|
|
|
|
extension CALayer {
|
|
|
|
/// 开始旋转
|
|
/// - Parameters:
|
|
/// - direction: 方向
|
|
/// - speed: 速度
|
|
func startRotate(direction: ProHUD.RotateDirection, speed: CFTimeInterval) {
|
|
if animation(forKey: .rotateKey) == nil {
|
|
let ani = CABasicAnimation(keyPath: "transform.rotation.z")
|
|
ani.toValue = direction.rawValue * Double.pi * 2.0
|
|
if speed > 0 {
|
|
ani.duration = 2 / speed
|
|
}
|
|
ani.repeatDuration = .infinity
|
|
add(ani, forKey: .rotateKey)
|
|
}
|
|
}
|
|
|
|
/// 停止旋转
|
|
func stopRotate() {
|
|
removeAnimation(forKey: .rotateKey)
|
|
}
|
|
|
|
}
|