MG--Swift遮照HUD 1

MG--Swift遮照HUD 1

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

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


2种遮照效果

image

image

代码实现如下

//  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
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容