jsdw_ios/QuickLocation/UIKit/HUD/DLHUD.swift

536 lines
18 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.

//
// File.swift
// JHSDK
//
// Created by osell on 2023/3/24.
//
import UIKit
// MARK: - DLHUD
/// DLLoading/Toast
@objcMembers
public class DLHUD: NSObject {
/// HUD
public static var isShowing: Bool { DLHUDView.shared.alpha == 1 }
/// Loading
/// - Parameters:
/// - text:
/// - interaction: Loadingtrue
public static func showLoading(text: String? = nil,
inView: UIView? = nil,
completion: (() -> Void)? = nil) {
DLHUDView.shared.setupHUD(text: text,
style: .loading,
inView: inView)
}
/// HUD
/// - Parameters:
/// - text:
/// - interaction: Loadingtrue
public static func showInfo(text: String,
interaction: Bool = true,
inView: UIView? = nil,
completion: (() -> Void)? = nil) {
DLHUDView.shared.setupHUD(text: text,
style: .info,
interaction: interaction,
inView: inView)
let delay = Double(text.count) * 0.03 + 1.25
DLHUDView.shared.dismissHUD(delay: delay, completion: completion)
}
/// HUD
/// - Parameter text:
public static func showSuccess(text: String? = nil,
inView: UIView? = nil,
completion: (() -> Void)? = nil) {
DLHUDView.shared.setupHUD(text: text,
style: .success,
interaction: true,
inView: inView)
let text = text ?? ""
let delay = Double(text.count) * 0.03 + 1.25
DLHUDView.shared.dismissHUD(delay: delay, completion: completion)
}
/// HUD
/// - Parameter text:
public static func showError(text: String? = nil,
inView: UIView? = nil,
completion: (() -> Void)? = nil) {
DLHUDView.shared.setupHUD(text: text,
style: .error,
interaction: true,
inView: inView)
let text = text ?? ""
let delay = Double(text.count) * 0.03 + 1.25
DLHUDView.shared.dismissHUD(delay: delay, completion: completion)
}
/// HUD
/// - Parameters:
/// - text:
/// - progress: 0 1.0
public static func showProgress(text: String? = nil,
progress: CGFloat,
inView: UIView? = nil,
completion: (() -> Void)? = nil) {
if progress >= 0 && progress <= 1 {
DLHUDView.shared.setupHUD(text: text,
style: .progress(progress: progress),
inView: inView)
}
if progress >= 1 || progress < 0 {
DLHUDView.shared.dismissHUD(completion: completion)
}
}
/// HUD
public static func dismiss(delay: TimeInterval = 0,
completion: (() -> Void)? = nil) {
DLHUDView.shared.dismissHUD(delay: delay, completion: completion)
}
}
// MARK: - DLHUDConfig
@objcMembers
public class DLHUDConfig {
///
public static let shared = DLHUDConfig()
/// HUD
public var backgroundStyle: UIBarStyle = .black
/// HUD
public var textColor: UIColor = .white
/// HUD
public var textFont: UIFont = .boldSystemFont(ofSize: 13)
/// HUD Progress
public var progress: UIColor = .white
/// Success
public var successImage: UIImage?
/// Error
public var errorImage: UIImage?
private init() {}
}
// MARK: - DLHUDView
fileprivate class DLHUDView: UIView {
// HUD
enum Style {
case info, //
loading, // loading
success, //
error, //
progress(progress: CGFloat) //
}
///
static let shared = DLHUDView()
var completeBlock: (() -> Void)?
///
lazy var textLabel: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.baselineAdjustment = .alignCenters
label.numberOfLines = 0
label.font = UIFont.systemFont(ofSize: 15)
return label
}()
///
lazy var iconImageView: UIImageView = {
let view = UIImageView(frame: CGRect(x: 0, y: 0, width: 60, height: 60))
view.contentMode = .scaleAspectFit
return view
}()
/// view
lazy var progressView: DLProgressView = {
let view = DLProgressView()
return view
}()
/// View
lazy var animationView: UIView = {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 60, height: 60))
return view
}()
/// hudToolBar
lazy var hudToolBar: UIToolbar = {
let view = UIToolbar()
view.isTranslucent = true
view.clipsToBounds = true
view.layer.cornerRadius = 8
view.layer.masksToBounds = true
return view
}()
///
lazy var backgroundView: UIView = {
let view = UIView()
return view
}()
///
private var timer: Timer?
convenience private init() {
self.init(frame: UIScreen.main.bounds)
alpha = 0
}
override private init(frame: CGRect) {
super.init(frame: frame)
}
required internal init?(coder: NSCoder) {
super.init(coder: coder)
}
}
// MARK: - Method
private extension DLHUDView {
/// HUD
/// - Parameters:
/// - text:
/// - style:
/// - interaction:
/// - isAutoHide:
/// - inView: View
func setupHUD(text: String? = nil,
style: DLHUDView.Style,
interaction: Bool = false,
inView: UIView? = nil) {
DispatchQueue.main.async {
self.hudToolBar.layer.removeAllAnimations()
self.setupBackgoundView(with: interaction, in: inView)
self.setupToolBar()
self.removeOtherView(with: style)
switch style {
case .progress(let progress):
self.setupTextLabel(with: text)
self.setupProgressView(with: progress)
case .error, .success:
self.setupImageView(with: style)
self.setupTextLabel(with: text)
case .loading:
self.setupLoadingView()
self.setupTextLabel(with: text)
default:
self.setupTextLabel(with: text)
}
//
self.setupSize(with: style)
self.displayHUD()
}
}
/// HUD
func displayHUD() {
synchronized(self.hudToolBar) {
if self.alpha == 0 {
self.alpha = 1
self.hudToolBar.alpha = 0
self.hudToolBar.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
UIView.animate(withDuration: 0.15, delay: 0, options: [.allowUserInteraction, .curveEaseIn]) {
self.hudToolBar.alpha = 1
self.hudToolBar.transform = CGAffineTransform.identity
}
}
}
}
/// HUD
func dismissHUD(delay: TimeInterval = 0, completion: (() -> Void)? = nil) {
if timer != nil {
timer?.invalidate()
timer = nil
}
self.timer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { [weak self] _ in
guard let self = self else { return }
DispatchQueue.main.async {
// synchronized(self.hudToolBar) {
if self.alpha == 1 {
UIView.animate(withDuration: 0.15, delay: 0, options: [.allowUserInteraction, .curveEaseIn]) {
self.hudToolBar.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
self.hudToolBar.alpha = 0
} completion: { _ in
self.iconImageView.removeFromSuperview()
self.animationView.removeFromSuperview()
self.progressView.removeFromSuperview()
self.progressView.setProgress(0)
self.hudToolBar.transform = CGAffineTransform.identity
self.hudToolBar.removeFromSuperview()
self.backgroundView.removeFromSuperview()
self.textLabel.removeFromSuperview()
self.alpha = 0
self.removeFromSuperview()
completion?()
}
}
// }
}
}
#if swift(>=4.2)
RunLoop.current.add(self.timer!, forMode: .common)
#else
RunLoop.current.add(self.timer!, forMode: .commonModes)
#endif
}
}
// MARK: - Private Method
private extension DLHUDView {
/// view
func setupBackgoundView(with interaction: Bool, in superView: UIView? = nil) {
guard let superTempView = superView ?? UIApplication.keyWindow else { return }
if backgroundView.superview == nil || backgroundView.superview != superTempView {
backgroundView.removeFromSuperview()
backgroundView.frame = superTempView.bounds
superTempView.addSubview(backgroundView)
}
backgroundView.backgroundColor = .clear
backgroundView.isUserInteractionEnabled = !interaction
}
/// hudToolBar
func setupToolBar() {
if hudToolBar.superview == nil {
hudToolBar.frame = .zero
backgroundView.addSubview(hudToolBar)
}
hudToolBar.barStyle = DLHUDConfig.shared.backgroundStyle
hudToolBar.backgroundColor = UIColor.white
}
/// Text
func setupTextLabel(with text: String?) {
if textLabel.superview == nil {
hudToolBar.addSubview(textLabel)
}
textLabel.text = text
textLabel.textColor = DLHUDConfig.shared.textColor
textLabel.font = DLHUDConfig.shared.textFont
textLabel.isHidden = !(text?.isEmpty != true)
}
/// View
func setupCustomView(with view: UIView) {
if view.superview == nil {
hudToolBar.addSubview(view)
}
}
/// Progress
/// - Parameter progress: CGFloat
func setupProgressView(with progress: CGFloat) {
if progressView.superview == nil {
progressView.frame = CGRect(x: 0, y: 0, width: 60, height: 60)
hudToolBar.addSubview(progressView)
}
progressView.setProgress(progress)
progressView.color = DLHUDConfig.shared.progress
}
///
func setupImageView(with style: DLHUDView.Style) {
if iconImageView.superview == nil {
hudToolBar.addSubview(iconImageView)
}
switch style {
case .success:
iconImageView.image = DLHUDConfig.shared.successImage ?? UIImage(named: "hud_success")
case .error:
iconImageView.image = DLHUDConfig.shared.errorImage ?? UIImage(named: "hud_error")
default:
iconImageView.image = nil
}
}
/// Loading
func setupLoadingView() {
animationView.subviews.forEach { $0.removeFromSuperview() }
if animationView.superview == nil {
hudToolBar.addSubview(animationView)
}
let spinner = UIActivityIndicatorView(style: .whiteLarge)
spinner.frame = animationView.bounds
spinner.color = .lightGray
spinner.hidesWhenStopped = true
spinner.startAnimating()
animationView.addSubview(spinner)
}
/// view
/// - Parameter style: Style
func removeOtherView(with style: DLHUDView.Style) {
switch style {
case .info:
animationView.removeFromSuperview()
progressView.removeFromSuperview()
iconImageView.removeFromSuperview()
textLabel.removeFromSuperview()
case .loading:
progressView.removeFromSuperview()
iconImageView.removeFromSuperview()
textLabel.removeFromSuperview()
case .success, .error:
animationView.removeFromSuperview()
progressView.removeFromSuperview()
textLabel.removeFromSuperview()
case .progress:
animationView.removeFromSuperview()
iconImageView.removeFromSuperview()
textLabel.removeFromSuperview()
}
}
/// Hudsize
/// - Parameter style: Style
func setupSize(with style: DLHUDView.Style) {
var hudWidth: CGFloat = 120
var hudHeight: CGFloat = 120
let screenSize = backgroundView.superview?.frame.size ?? UIScreen.main.bounds.size
let maxWidth = min(screenSize.width, screenSize.height) - 80
if let text = textLabel.text, !text.isEmpty {
var textRect = text.boundingRect(with: CGSize(width: maxWidth, height: screenSize.height * 0.65),
options: .usesLineFragmentOrigin,
attributes: [NSAttributedString.Key.font : DLHUDConfig.shared.textFont],
context: nil)
hudWidth = ceil(textRect.size.width) + 40
hudHeight = ceil(textRect.size.height)
if hudWidth < 120 { hudWidth = 120 }
textRect.origin.x = (hudWidth - textRect.size.width) / 2
switch style {
case .info:
hudHeight = hudHeight + 40
textRect.origin.y = (hudHeight - textRect.size.height) / 2
default:
textRect.origin.y = 90
hudHeight = hudHeight + 110
}
textLabel.frame = textRect
}
hudToolBar.bounds = CGRect(origin: .zero, size: CGSize(width: hudWidth, height: hudHeight))
let centerX = hudWidth / 2
var centerY = hudWidth / 2
if textLabel.text != nil { centerY = 50 }
progressView.center = CGPoint(x: centerX, y: centerY)
animationView.center = CGPoint(x: centerX, y: centerY)
iconImageView.center = CGPoint(x: centerX, y: centerY)
hudToolBar.center = CGPoint(x: screenSize.width / 2, y: screenSize.height / 2)
}
}
// MARK: - DLProgressView
public class DLProgressView: UIView {
public var color: UIColor = .white {
didSet { setupLayers() }
}
private var progress: CGFloat = 0
private var layerCircle = CAShapeLayer()
private var layerProgress = CAShapeLayer()
private var labelPercentage: UILabel = UILabel()
convenience init(_ color: UIColor) {
self.init(frame: .zero)
self.color = color
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
public override func draw(_ rect: CGRect) {
super.draw(rect)
setupLayers()
}
func setupLayers() {
subviews.forEach { $0.removeFromSuperview() }
layer.sublayers?.forEach { $0.removeFromSuperlayer() }
let width = frame.size.width
let height = frame.size.height
let center = CGPoint(x: width/2, y: height/2)
let radiusCircle = width / 2
let radiusProgress = width / 2
let pathCircle = UIBezierPath(arcCenter: center, radius: radiusCircle, startAngle: -0.5 * .pi, endAngle: 1.5 * .pi, clockwise: true)
let pathProgress = UIBezierPath(arcCenter: center, radius: radiusProgress, startAngle: -0.5 * .pi, endAngle: 1.5 * .pi, clockwise: true)
layerCircle.path = pathCircle.cgPath
layerCircle.fillColor = UIColor.clear.cgColor
layerCircle.lineWidth = 3
layerCircle.strokeColor = color.withAlphaComponent(0.2).cgColor
layerProgress.path = pathProgress.cgPath
layerProgress.fillColor = UIColor.clear.cgColor
layerProgress.lineWidth = 4
layerProgress.strokeColor = color.cgColor
layerProgress.strokeEnd = 0
layer.addSublayer(layerCircle)
layer.addSublayer(layerProgress)
labelPercentage.frame = self.bounds
labelPercentage.textColor = color
labelPercentage.textAlignment = .center
addSubview(labelPercentage)
}
public func setProgress(_ value: CGFloat, duration: TimeInterval = 0.2) {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = duration
animation.fromValue = progress
animation.toValue = value
animation.fillMode = .both
animation.isRemovedOnCompletion = false
layerProgress.add(animation, forKey: "animation")
progress = value
labelPercentage.text = "\(Int(value*100))%"
}
}