在项目中经常会遇到比较耗时的操作
比如网络请求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
}
}
}
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦