手记

MG--Swift遮照HUD 1

在项目中经常会遇到比较耗时的操作

比如网络请求IO读写操作这个操作往往比较耗时这个时候我们往往不需要用户操作这个时候我们就应该提供一个蒙版遮照来防止用户操作就需要用到下面的方法了下面是通过协议的方式写的蒙版提示界面我们一般每写的一个界面度会涉及到网络APi请求所以我们可以写一个公共的基类比如这里的BaseViewController我们只需要它遵守协议其他界面需要HUD提示的继承此控制器即可


2种遮照效果

💪

代码实现如下

//  MGCommonActivityIndicatorConfig.swift
//  MGSDKDemo
//  Created by LYM-mg on 2021/1/25.
//  Copyright © 2021 . All rights reserved.


import UIKit

fileprivate let kIndicatorViewLength = CGFloat(155)
//Common
fileprivate let kCommonAnimationTime:Double = 0.24
fileprivate let SpinningAnimationKey = "com.hsbc.loading.view.spinning.animation.key"
fileprivate let CurveEndAnimationKey = "com.hsbc.loading.view.curve.end.animation.key"
fileprivate let CurveStartAnimationKey = "com.hsbc.loading.view.curve.start.animation.key"

fileprivate let kViewLength = CGFloat(45)
fileprivate let kPartialCircleStrokeEnd = CGFloat(0.08)
fileprivate let kBackgroundCornerRadius = CGFloat(0)
fileprivate let kBackgroundViewPadding = CGFloat(10)
fileprivate let kSpinningDuration = TimeInterval(3.24)
fileprivate let kCircleLineWidth = CGFloat(3.5)
fileprivate let kPartialCircleLineWidth = CGFloat(3)

public enum ActivityIndicatorType {
    case blocking
    case unblocking(style: UIActivityIndicatorView.Style)
}

public protocol MGCommonActivityIndicatorConfig: class {
    var _indicatorContainerView: MGIndicatorContainerView? { get set }
    var _hudView: MGHUDView? { get set }
}

// MARK: Activity Indicator
extension MGCommonActivityIndicatorConfig where Self: UIViewController {
    // MARK: - indicatorContainerView
    var indicatorContainerView: MGIndicatorContainerView {
        if let indicatorContainerView = _indicatorContainerView, let superView = indicatorContainerView.superview {
            superView.bringSubviewToFront(indicatorContainerView)
            return indicatorContainerView
        } else {
            let indicatorContainerView = MGIndicatorContainerView()
            indicatorContainerView.frame = CGRect(x: 0, y: 0, width: kIndicatorViewLength, height: kIndicatorViewLength)
            let view: UIView
            if let parentViewController = self.parent, parentViewController is BaseViewController {
                view = parentViewController.view
            }
            else {
                view = self.view
            }
            
            if let window = UIApplication.shared.keyWindow {
                let frame = window.convert(window.frame, to: view)
                let centerX = frame.origin.x + (UIScreen.main.bounds.width / 2)
                let centerY = frame.origin.y  + (UIScreen.main.bounds.height / 2)
                indicatorContainerView.center = CGPoint(x: centerX, y: centerY)
            } else {
                indicatorContainerView.center = view.center
                
                if #available(iOS 11, *) {
                    indicatorContainerView.center.y -= self.navigationController == nil ? 0 : self.view.safeAreaInsets.top
                }
                    
                else {
                    indicatorContainerView.center.y -= self.navigationController == nil ? 0 : 64
                }
                
                indicatorContainerView.center.y += self.topLayoutGuide.length
            }
            view.addSubview(indicatorContainerView)
            
            indicatorContainerView.indicatorView.hidesWhenStopped = true
            _indicatorContainerView = indicatorContainerView
            return indicatorContainerView
        }
    }
    
    public func showActivityIndicator(_ type: ActivityIndicatorType = .blocking, backgroundStyle: MGIndicatorView.SpinBackgroundStyle = .white, showInParnetViewControllerIfPossible: Bool = true, completion: ((Bool) -> Void)? = nil) {
        if let parentViewController = self.parent as? BaseViewController, showInParnetViewControllerIfPossible == true {
            parentViewController.showActivityIndicator()
            return
        }
        
        switch type {
            case .blocking:
                self.indicatorContainerView.indicatorView.setBlocking(self.view, type: type)
            default: break
        }
        
        self.indicatorContainerView.configLayout(backgroundStyle)
        
        self.indicatorContainerView.startAnimating(backgroundStyle, completion: completion)
    }
    
    public func hideActivityIndicator(_ hideInParnetViewControllerIfPossible: Bool = true) {
        if let parentViewController = self.parent as? BaseViewController, hideInParnetViewControllerIfPossible == true {
            parentViewController.hideActivityIndicator()
            return
        }
        
        self.indicatorContainerView.stopAnimation({ [weak self] finished in
            guard let strongSelf = self else { return }
            strongSelf.indicatorContainerView.indicatorView.setBlocking(strongSelf.view, type: .unblocking(style: .medium))
        })
    }
    
    public func showActivityIndicatorForNav() {
        if let nav = self.navigationController as? MGNavigationController {
            nav.showActivityIndicator(.blocking)
            nav.navigationBar.isUserInteractionEnabled = false
            nav.view.isUserInteractionEnabled = false
        } else {
            self.showActivityIndicator(.blocking)
        }
    }
    
    public func hideActivityIndicatorForNav() {
        if let nav = self.navigationController as? MGNavigationController {
            nav.hideActivityIndicator()
            nav.navigationBar.isUserInteractionEnabled = true
            nav.view.isUserInteractionEnabled = true
        } else {
            self.hideActivityIndicator()
        }
    }
    
    
    // MARK: - HUD
    var hudView: MGHUDView {
        if let hudView = _hudView, let superView = hudView.superview {
            superView.bringSubviewToFront(hudView)
            return hudView
        } else {
            let hudView = MGHUDView(frame: self.view.frame)
            let view: UIView
            if let parentViewController = self.parent, parentViewController is BaseViewController {
                view = parentViewController.view
            }
            else {
                view = self.view
            }
            
            view.addSubview(hudView)
            _hudView = hudView
            return hudView
        }
    }
    
    public func showMGHUD(_ type: ActivityIndicatorType = .blocking, hudType: MGHUDType = .MGHUDTypeCenter, completion: ((Bool) -> Void)? = nil) {
        self.hudView.configHUDType(type: hudType)
        
        switch type {
            case .blocking:
                self.hudView.setBlocking(self.view, type: type)
            default: break
        }
        self.hudView.startAnimating()
    }
    
    public func hideMGHUD() {
        self.hudView.stopAnimating { [weak self] in
            guard let strongSelf = self else { return }
            strongSelf.hudView.setBlocking(strongSelf.view, type: .unblocking(style: .medium))
        }
    }
    
}



// MARK: - MGIndicatorContainerView
public class MGIndicatorContainerView: UIView {
    
    weak var backgroundContainerView: UIView!
    weak var backgroundOverlayView: UIView!
    weak var containerView: UIView!
    
    weak var indicatorView: MGIndicatorView!
    weak var titleLabel: UILabel!
    
    weak var blurView: UIVisualEffectView!
    
    var isAnimating: Bool = false
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        self.commonInit()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension MGIndicatorContainerView {
    fileprivate func commonInit() {
        self.isHidden = true
        
        let blurView = UIVisualEffectView(effect: nil)
        blurView.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(blurView)
        self.blurView = blurView
        
        let backgroundContainerView = UIView()
        backgroundContainerView.translatesAutoresizingMaskIntoConstraints = false
        backgroundContainerView.clipsToBounds = true
        self.addSubview(backgroundContainerView)
        self.backgroundContainerView = backgroundContainerView
        
        let backgroundOverlayView = UIView()
        backgroundOverlayView.translatesAutoresizingMaskIntoConstraints = false
        backgroundOverlayView.backgroundColor = UIColor(red: 0xA7/255.0, green: 0xA7/255.0, blue: 0xA7/255.0, alpha: 1.0)
        backgroundOverlayView.alpha = 0.25
        self.backgroundContainerView.addSubview(backgroundOverlayView)
        self.backgroundOverlayView = backgroundOverlayView
        
        let containerView = UIView()
        containerView.translatesAutoresizingMaskIntoConstraints = false
        containerView.backgroundColor = UIColor.clear
        self.backgroundContainerView.addSubview(containerView)
        self.containerView = containerView
        
        let indicatorView = MGIndicatorView()
        indicatorView.translatesAutoresizingMaskIntoConstraints = false
        self.containerView.addSubview(indicatorView)
        self.indicatorView = indicatorView
        
        let titleLabel = UILabel()
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.font = UIFont(name: "UniversNextforHSBC-Regular", size: 12)
        self.containerView.addSubview(titleLabel)
        self.titleLabel = titleLabel
        
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[backgroundContainerView]|", options: [], metrics: nil, views: ["backgroundContainerView": backgroundContainerView]))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[backgroundContainerView]|", options: [], metrics: nil, views: ["backgroundContainerView": backgroundContainerView]))
        
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[blurView]|", options: [], metrics: nil, views: ["blurView": blurView]))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[blurView]|", options: [], metrics: nil, views: ["blurView": blurView]))
        
        self.backgroundContainerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[backgroundOverlayView]|", options: [], metrics: nil, views: ["backgroundOverlayView": backgroundOverlayView]))
        self.backgroundContainerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[backgroundOverlayView]|", options: [], metrics: nil, views: ["backgroundOverlayView": backgroundOverlayView]))
        
        self.backgroundContainerView.addConstraint(NSLayoutConstraint(item: self.containerView, attribute: .centerX, relatedBy: .equal, toItem: self.backgroundContainerView, attribute: .centerX, multiplier: 1, constant: 0))
        self.backgroundContainerView.addConstraint(NSLayoutConstraint(item: self.containerView, attribute: .centerY, relatedBy: .equal, toItem: self.backgroundContainerView, attribute: .centerY, multiplier: 1, constant: 0))
        
        self.containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[indicatorView(==45)]-20-[titleLabel]|", options: [], metrics: nil, views: ["indicatorView": indicatorView, "titleLabel": titleLabel]))
        self.containerView.addConstraint(NSLayoutConstraint(item: self.indicatorView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 45))
        self.containerView.addConstraint(NSLayoutConstraint(item: self.indicatorView, attribute: .centerX, relatedBy: .equal, toItem: self.containerView, attribute: .centerX, multiplier: 1, constant: 0))
        self.containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[titleLabel]|", options: [], metrics: nil, views: [
            "titleLabel": titleLabel]))
    }
}

extension MGIndicatorContainerView {
    func configLayout(_ backgroundType: MGIndicatorView.SpinBackgroundStyle) {
        switch backgroundType {
        case .white:
            self.backgroundOverlayView.backgroundColor = UIColor(red: 0xA7/255.0, green: 0xA7/255.0, blue: 0xA7/255.0, alpha: 1.0)
            self.backgroundOverlayView.alpha = 0.25
            self.indicatorView.strokeColor = UIColor(red: 0x5C/255.0, green: 0x5C/255.0, blue: 0x5C/255.0, alpha: 1.0)
            self.titleLabel.textColor = UIColor(red: 0x5C/255.0, green: 0x5C/255.0, blue: 0x5C/255.0, alpha: 1.0)
        case .black:
            self.backgroundOverlayView.backgroundColor = UIColor(white: 0, alpha: 0.25)
            self.indicatorView.strokeColor = .white
            self.titleLabel.textColor = .white
        }
        
        self.titleLabel.text = "Loading"
    }
}

extension MGIndicatorContainerView {
    func startAnimating(_ backgroundType: MGIndicatorView.SpinBackgroundStyle, completion: ((Bool) -> Void)? = nil) {
        if isAnimating {
            DispatchQueue.main.asyncAfter(deadline: .now() + kCommonAnimationTime) {
                self.startAnimating(backgroundType, completion: completion)
            }
            return
        }
        isAnimating = true
        let effect: UIBlurEffect!
        switch backgroundType {
        case .white:
            effect = UIBlurEffect(style: .light)
        case .black:
            effect = UIBlurEffect(style: .dark)
        }
        
        self.indicatorView.startAnimating()
        self.isHidden = false
        self.backgroundContainerView.alpha = 0
        UIView.animate(withDuration: kCommonAnimationTime, animations: {
            self.backgroundContainerView.alpha = 1
            self.blurView.effect = effect
            self.isAnimating = false
        }, completion: completion)
    }
    
    func stopAnimation(_ completion: ((Bool) -> ())? = nil) {
        if isAnimating {
            DispatchQueue.main.asyncAfter(deadline: .now() + kCommonAnimationTime*2) {
                self.stopAnimation(completion)
            }
            return
        }
        self.backgroundContainerView.alpha = 1
        isAnimating = true
        UIView.animate(withDuration: kCommonAnimationTime, animations: {
            self.backgroundContainerView.alpha = 0
            self.blurView.effect = nil
        }, completion: { (finished) in
            self.isHidden = true
            self.indicatorView.stopAnimating()
            self.isAnimating = false
            completion?(finished)
        })
    }
}


// MARK: - MGIndicatorView
@IBDesignable open class MGIndicatorView: UIView {
    public enum SpinBackgroundStyle {
        case white
        case black
    }
    
    @IBInspectable open var lineWidth: CGFloat = kPartialCircleLineWidth {
        didSet {
            self.partialCirclePathLayer.lineWidth = self.lineWidth
        }
    }
    fileprivate(set) open var isAnimating = false
    @IBInspectable open var autoStartAnimating: Bool = false {
        didSet {
            if self.autoStartAnimating && self.superview != nil {
                self.animate(true)
            }
        }
    }
    @IBInspectable open var hidesWhenStopped: Bool = false
    @IBInspectable open var strokeColor: UIColor = UIColor(red: 0x5C/255.0, green: 0x5C/255.0, blue: 0x5C/255.0, alpha: 1.0) {
        didSet {
            self.partialCirclePathLayer.strokeColor = self.strokeColor.cgColor
        }
    }
    
    fileprivate var partialCirclePathLayer = CAShapeLayer()
    fileprivate var circlePathLayer = CAShapeLayer()
    weak var spinBackgroundView: UIView!
    fileprivate weak var imageView: UIImageView!
    open var loadingImage: UIImage? {
        didSet {
            self.imageView.image = self.loadingImage
        }
    }
    
    fileprivate var needReloadCurveAnimation = false
    fileprivate var isFirstLaunch = true
    
    deinit {
        NotificationCenter.default.removeObserver(self, name: UIApplication.didBecomeActiveNotification, object: nil)
    }
    
    override public init(frame: CGRect) {
        super.init(frame: frame)
        configure()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }
    
    override open func layoutSubviews() {
        super.layoutSubviews()
        self.partialCirclePathLayer.frame = bounds
        self.partialCirclePathLayer.path = self.circlePath().cgPath
        self.partialCirclePathLayer.strokeStart = 0
        self.partialCirclePathLayer.strokeEnd = 0
        
        self.circlePathLayer.frame = bounds
        self.circlePathLayer.path = self.circlePath().cgPath
        var circleFrame = self.circleFrame()
        let radius = min(self.partialCirclePathLayer.bounds.size.width, self.partialCirclePathLayer.bounds.size.height) / 2
        circleFrame = circleFrame.insetBy(dx: radius/2, dy: radius/2)
        let spinBackgroundViewLength = kViewLength + kBackgroundViewPadding * 2
        self.spinBackgroundView.frame = CGRect(x: -kBackgroundViewPadding, y: -kBackgroundViewPadding, width: spinBackgroundViewLength, height: spinBackgroundViewLength)
        self.imageView.frame = circleFrame
        
        self.isFirstLaunch = false
    }
    
    override open func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        
        if newSuperview != nil {
            if self.autoStartAnimating {
                self.animate(true)
            }
        } else {
            self.animate(false)
        }
    }
    
    override open func willMove(toWindow newWindow: UIWindow?) {
        super.willMove(toWindow: newWindow)
        
        if self.needReloadCurveAnimation {
            self.needReloadCurveAnimation = false
            
            self.resetCurveAnimation()
        }
    }
    
    open func startAnimating() {
        self.animate(true)
    }
    
    open func stopAnimating() {
        self.animate(false)
    }
    
    fileprivate func animate(_ animated: Bool) {
        if animated {
            self.isHidden = false
            
            if self.isAnimating == false {
                self.createAnimationLayer()
            }
            
            self.isAnimating = true
        } else {
            self.isAnimating = false
            if self.hidesWhenStopped {
                self.isHidden = true
            }
            
            self.partialCirclePathLayer.removeAnimation(forKey: SpinningAnimationKey)
            self.partialCirclePathLayer.removeAnimation(forKey: CurveEndAnimationKey)
            self.partialCirclePathLayer.removeAnimation(forKey: CurveStartAnimationKey)
        }
    }
    
    func removeAllAnimationLayer() {
        self.partialCirclePathLayer.removeAnimation(forKey: SpinningAnimationKey)
        self.partialCirclePathLayer.removeAnimation(forKey: CurveEndAnimationKey)
        self.partialCirclePathLayer.removeAnimation(forKey: CurveStartAnimationKey)
    }
    
    func createAnimationLayer() {
        self.partialCirclePathLayer.strokeStart = 0
        self.partialCirclePathLayer.strokeEnd = 0
        
        let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotateAnimation.fromValue = NSNumber(value: 0)
        rotateAnimation.toValue = NSNumber(value: 2 * Double.pi)
        rotateAnimation.duration = kSpinningDuration
        rotateAnimation.isRemovedOnCompletion = false // prevent from getting remove when app enter background or view disappear
        rotateAnimation.repeatCount = Float.infinity
        self.partialCirclePathLayer.add(rotateAnimation, forKey: SpinningAnimationKey)
        
        self.createCurveChangeAnimationLayer(self.partialCirclePathLayer)
    }
    
    fileprivate func createCurveChangeAnimationLayer(_ layer: CALayer) {
        let curveEndChangeAnimation = CABasicAnimation(keyPath: "strokeEnd")
        curveEndChangeAnimation.fromValue = 0
        curveEndChangeAnimation.toValue = 1
        curveEndChangeAnimation.duration = 1.7
        curveEndChangeAnimation.fillMode = CAMediaTimingFillMode.forwards
        curveEndChangeAnimation.isRemovedOnCompletion = false
        curveEndChangeAnimation.repeatCount = 0
        curveEndChangeAnimation.isCumulative = true
        curveEndChangeAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
        self.partialCirclePathLayer.add(curveEndChangeAnimation, forKey: CurveEndAnimationKey)
        
        let curveStartChangeAnimation = CABasicAnimation(keyPath: "strokeStart")
        curveStartChangeAnimation.fromValue = self.partialCirclePathLayer.strokeStart
        self.partialCirclePathLayer.strokeStart = 0
        self.partialCirclePathLayer.strokeEnd = 0
        curveStartChangeAnimation.toValue = 1
        curveStartChangeAnimation.beginTime = CACurrentMediaTime() + 0.24
        curveStartChangeAnimation.duration = 1.7
        curveStartChangeAnimation.isRemovedOnCompletion = false
        curveStartChangeAnimation.delegate = self
        curveStartChangeAnimation.repeatCount = 0
        curveStartChangeAnimation.isCumulative = true
        curveStartChangeAnimation.fillMode = CAMediaTimingFillMode.forwards
        curveStartChangeAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
        self.partialCirclePathLayer.add(curveStartChangeAnimation, forKey: CurveStartAnimationKey)
    }
    
    fileprivate func circleFrame() -> CGRect {
        // Align center
        let diameter = min(self.partialCirclePathLayer.bounds.size.width, self.partialCirclePathLayer.bounds.size.height)
        var circleFrame = CGRect(x: 0, y: 0, width: diameter, height: diameter)
        circleFrame.origin.x = self.partialCirclePathLayer.bounds.midX - circleFrame.midX
        circleFrame.origin.y = self.partialCirclePathLayer.bounds.midY - circleFrame.midY
        
        // offset lineWidth
        let inset = self.partialCirclePathLayer.lineWidth / 2
        circleFrame = circleFrame.insetBy(dx: inset, dy: inset)
        
        return circleFrame
    }
    
    fileprivate func circlePath() -> UIBezierPath {
        return UIBezierPath(ovalIn: self.circleFrame())
    }
    
    fileprivate func configure() {
        self.bounds = CGRect(x: 0, y: 0, width: kViewLength, height: kViewLength)
        self.backgroundColor = UIColor.clear
        
        let spinBackgroundView = UIView()
        spinBackgroundView.backgroundColor = UIColor(white: 1, alpha: 0.8)
        spinBackgroundView.layer.cornerRadius = kBackgroundCornerRadius
        spinBackgroundView.isHidden = true
        self.addSubview(spinBackgroundView)
        self.spinBackgroundView = spinBackgroundView
        
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFit
        imageView.backgroundColor = UIColor.clear
        self.addSubview(imageView)
        self.imageView = imageView
        
//        self.circlePathLayer.frame = bounds
//        self.circlePathLayer.backgroundColor = UIColor.clear.cgColor
//        self.circlePathLayer.lineWidth = kCircleLineWidth
//        self.circlePathLayer.fillColor = UIColor.clear.cgColor
//        self.circlePathLayer.strokeColor = defaultLayout().colorWithColorIdentifier(ColorIdentifier.divider).cgColor
//        self.circlePathLayer.opacity = 0.8
//        layer.addSublayer(self.circlePathLayer)
        
        self.setUpPartialCirclePathLayer()
        self.layoutSubviews()
    }
    
    func setUpPartialCirclePathLayer() {
        self.partialCirclePathLayer.frame = bounds
        self.partialCirclePathLayer.lineWidth = kPartialCircleLineWidth
        self.partialCirclePathLayer.fillColor = UIColor.clear.cgColor
        self.partialCirclePathLayer.strokeColor = self.strokeColor.cgColor
        self.partialCirclePathLayer.strokeStart = 0
        self.partialCirclePathLayer.strokeEnd = 0
        self.partialCirclePathLayer.backgroundColor = UIColor.clear.cgColor
        layer.addSublayer(self.partialCirclePathLayer)
    }
    
    fileprivate func resetCurveAnimation() {
        self.partialCirclePathLayer.strokeStart = 0
        self.partialCirclePathLayer.strokeEnd = 0
        
        self.partialCirclePathLayer.removeAnimation(forKey: CurveEndAnimationKey)
        self.partialCirclePathLayer.removeAnimation(forKey: CurveStartAnimationKey)
        
        self.createCurveChangeAnimationLayer(self.partialCirclePathLayer)
    }
}

extension MGIndicatorView: CAAnimationDelegate {
    public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        if flag {
            self.resetCurveAnimation()
        } else {
            if self.isAnimating {
                self.needReloadCurveAnimation = true
            }
        }
    }
}

// MARK: - Blocking handling
extension MGIndicatorView {
    func setBlocking(_ view: UIView, type: ActivityIndicatorType) {
        switch type {
        case .blocking:
            view.isUserInteractionEnabled = false
        default:
            view.isUserInteractionEnabled = true
        }
    }
}

class BaseViewController: UIViewController, MGCommonActivityIndicatorConfig {
    var _hudView: MGHUDView?
    var _indicatorContainerView: MGIndicatorContainerView?
}

class MGNavigationController: UINavigationController, MGCommonActivityIndicatorConfig {
    var _indicatorContainerView: MGIndicatorContainerView?
    var _hudView: MGHUDView?
}


public enum MGHUDType {
    case MGHUDTypeCenter     //居中
    case MGHUDTypeFullScren  //全屏
}

public class MGHUDView: UIView {
    fileprivate var hudType: MGHUDType = .MGHUDTypeCenter {
        didSet {
            if hudType == .MGHUDTypeCenter {
                self.setupLoadingBackView(frame: CGRect(x: (self.frame.size.width-160)/2, y: (self.frame.size.height-160)/2-20, width: 160, height: 160), title: "")
                self.loadingBackView?.layer.cornerRadius = 10;
                self.loadingBackView?.backgroundColor = UIColor(red: 222/255.0, green: 222/255.0, blue: 222/255.0, alpha: 0.8)
            }else {
                self.setupLoadingBackView(frame: CGRect(x: (self.frame.size.width-160)/2, y: self.frame.size.height/3, width: 160, height: 160), title: "")
                self.backgroundColor = .white
            }
        }
    }
    var loadingBackView: UIView?
    var label: UILabel?
    var angle: Double = 0
    var titleString: String?
    var isAnimating: Bool = false
    var stepNumber: Int = 0
    var timer: Timer?
    
    var shapView: UIImageView?
    var shadowView: UIImageView?
    
    var fromValue: Float = 0
    var toValue: Float = 0
    var scalefromValue: Float = 0
    var scaletoValue: Float = 0
    
    deinit {
        self.timer?.invalidate()
        self.timer = nil
    }
    
    func setupLoadingBackView(frame: CGRect, title: String) {
        self.loadingBackView = UIView(frame: frame)
        self.addSubview(self.loadingBackView!)
        self.titleString = title;
        self.step()
    }
    
    func step() {
        guard let loadingBackView = self.loadingBackView  else {
            return
        }
        var l_width = loadingBackView.frame.size.width
        var l_height = loadingBackView.frame.size.height
        var isCenter = false
        if (self.hudType == .MGHUDTypeCenter) {
            
            l_width-=15;
            l_height-=15;
            isCenter = true;
        }
        shapView = UIImageView();
        shapView?.frame = CGRect(x: (l_width-35)/2+(isCenter ? 7.5 : 0), y: (isCenter ? 10 : 0), width: 35, height: 35)
        shapView?.image = UIImage(named: "loading_square")
        shapView?.contentMode = .scaleAspectFit;
        loadingBackView.addSubview(shapView!)
        
        //阴影
        shadowView = UIImageView()
        shadowView?.frame = CGRect(x: l_width/2-40/2+(isCenter ? 7.5:0), y: l_height-4-30, width: 40, height: 4)
        shadowView?.image = UIImage(named: "loading_shadow")
        loadingBackView.addSubview(shadowView!)
        
        label = UILabel()
        label?.frame = CGRect(x: (isCenter ? 7.5 : 0), y: l_height-20, width: l_width, height: 20)
        label?.textColor = UIColor(red: 55, green: 55, blue: 55, alpha: 1.0);
        label?.textAlignment = .center
        label?.font = UIFont.systemFont(ofSize: 13)
        label?.text = "加载中...";
        loadingBackView.addSubview(label!)

       if (self.hudType == .MGHUDTypeCenter) {
            
        }else {
            shapView?.backgroundColor = .white
        }
        
        fromValue = Float(shapView!.frame.size.height/2+(isCenter ? 10 : 0));
        toValue =  Float(l_height - 30 - shapView!.frame.size.height/2 - shadowView!.frame.size.height)
        
        
        scalefromValue = 0.3
        scaletoValue = 1.0
        self.angle = 0;
        self.alpha = 0;
    }
    
    func scaleAnimation(_ fromeValue: Float, toValue: Float, timingFunction tf: String?) {
        //缩放
        let sanimation = CABasicAnimation()
        sanimation.keyPath = "transform.scale"
        sanimation.fromValue = NSNumber(value: fromeValue)
        sanimation.toValue = NSNumber(value: toValue)
        sanimation.duration = 0.5

        sanimation.fillMode = .forwards
        sanimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName(rawValue: tf ?? CAMediaTimingFunctionName.easeIn.rawValue))
        sanimation.isRemovedOnCompletion = false

        shadowView?.layer.add(sanimation, forKey: "shadow")
    }
    
    @objc func animateNextStep() {
        switch stepNumber {
            case 0:
                loadingAnimation(fromValue, toValue: toValue, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
                scaleAnimation(scalefromValue, toValue: scaletoValue, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
            break;
            case 1:
                loadingAnimation(
                    toValue,
                    toValue: fromValue,
                    timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
                scaleAnimation(scaletoValue, toValue: scalefromValue, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
//                stepNumber = -1
            break;
            case 2:
                shapView!.image = UIImage(named: "loading_square")
                loadingAnimation(
                    fromValue,
                    toValue: toValue,
                    timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
            break;
            case 3:
                loadingAnimation(
                toValue,
                toValue: fromValue,
                timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
                stepNumber = -1
            break;
            default: break
        }
        stepNumber += 1
    }
        
    func loadingAnimation(_ fromValue: Float, toValue: Float, timingFunction tf: String) {
        let panimation = CABasicAnimation()
        panimation.keyPath = "position.y"
        panimation.fromValue = NSNumber(value: fromValue)
        panimation.toValue = NSNumber(value: toValue)
        panimation.duration = 0.5
        panimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName(rawValue: tf))
        
        //旋转
        let ranimation = CABasicAnimation()
        ranimation.keyPath = "transform.rotation"

        ranimation.fromValue = NSNumber(value: angle)
        ranimation.toValue = NSNumber(value: angle + Double.pi/2)
        angle += Double.pi/2
        ranimation.duration = 0.5
        ranimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName(rawValue: tf))
        
        //组合
        let group = CAAnimationGroup()
        group.animations = [panimation, ranimation]
        group.duration = 0.5
        group.beginTime = 0
        group.fillMode = .forwards
        group.isRemovedOnCompletion = false
        
        shapView!.layer.add(group, forKey: "basic")
    }
    
    public func configHUDType(type: MGHUDType) {
        self.hudType = type
        label?.text = "加载中...";
    }
    public func startAnimating() {

        if !isAnimating, timer == nil {
            isAnimating = true
            self.alpha = 1
            timer = Timer(timeInterval: 0.5, target: self, selector: #selector(animateNextStep), userInfo: nil, repeats: true)
            RunLoop.main.add(timer!, forMode: RunLoop.Mode.common)
        }
    }

    public func stopAnimating(_ completion: (() -> ())? = nil) {

        isAnimating = false
        timer?.invalidate()
        timer = nil

        stepNumber = 0

        alpha = 0
        
        shapView?.layer.removeAllAnimations()
        shadowView?.layer.removeAllAnimations()
        shapView?.image = UIImage(named: "loading_square")
        completion?()
        self.removeFromSuperview()
    }
    
    public func setBlocking(_ view: UIView, type: ActivityIndicatorType) {
        switch type {
        case .blocking:
            view.isUserInteractionEnabled = false
        default:
            view.isUserInteractionEnabled = true
        }
    }
}
0人推荐
随时随地看视频
慕课网APP