美文网首页
swift UIView雷达动画---支持开始/暂停/移除,以及

swift UIView雷达动画---支持开始/暂停/移除,以及

作者: flyrr | 来源:发表于2017-03-22 14:10 被阅读136次

首先写个空的协议

protocol Pulseable {}

然后,扩展Pulseable协议

在这里,说明一下。一般我们在为协议添加,实现默认参数时,有一个坑,需要注意下:

protocol AAAA {
    var name: String { get set }
var label: String { get set }
}
   通常我们会这样给AAAA协议添加默认参数name。这种做法在处理状态时,即返回的描述物体的状态值---(Int,Double,枚举值....),这是没有任何问题的。
extension AAAA {
    var name: String {
        return "Sarla"
    }
但是下面这种,返回的对象类型时,会出现不可预估的错误---即每次用到(.label)的label都是不一样的。
extension AAAA {
    var lable: UILabel {
        return UILabel()
    }
}
这是因为,我们只给它实现了get,而每次get的时候,都是直接生成一个新的UILabel对象,然后返回的。
我们可以打印下对象地址查看下,每次用到的时候是不一样的
print(Unmanaged.passUnretained(label).toOpaque())

正确的返回对象默认实现是下面这种的

private var pulseLayerKey: Void?
private let externalLayerName = "YGLayerName"
private let pulseKey = "PulseKey"
private let radarKey = "RadarKey"
extension Pulseable where Self: UIView { 
    /// 动画layer
    var animationLayer: CALayer {
        get {
            if let object = objc_getAssociatedObject(self, &pulseLayerKey) as? CALayer {
                return object
            }
            let externalBorder = CALayer()
            externalBorder.frame = frame
            externalBorder.cornerRadius = layer.cornerRadius
            externalBorder.name = externalLayerName
            objc_setAssociatedObject(self, &pulseLayerKey, externalBorder, .OBJC_ASSOCIATION_RETAIN)
            return externalBorder
        }
        set {
           objc_setAssociatedObject(self, &pulseLayerKey, newValue, .OBJC_ASSOCIATION_RETAIN)
        }
    }
   /// 添加脉冲动画
    ///
    /// - Parameters:
    ///   - color: 外层颜色
    ///   - startScale: 动画起始值
    ///   - finishScale: 动画到达值---默认是1.4
    ///   - frequency: 频率---默认是1.0
    ///   - opacity: 外层透明度---默认是0.7
    ///   - mode: 动画类型
    @discardableResult
    func addPulse(with color: UIColor, startScale: CGFloat, finishScale: CGFloat = 1.4, frequency: CFTimeInterval = 1.0, opacity: Float = 0.7, mode: PulseAnimationMode) -> Self {
        animationLayer.backgroundColor = color.cgColor
        animationLayer.opacity = opacity
        layer.masksToBounds = false
        layer.superlayer?.insertSublayer(animationLayer, below: layer)
        
        let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
        scaleAnimation.fromValue = startScale
        scaleAnimation.toValue = finishScale
        scaleAnimation.autoreverses = mode == .regular
        scaleAnimation.duration = frequency
        scaleAnimation.repeatCount = Float.greatestFiniteMagnitude
        animationLayer.add(scaleAnimation, forKey: pulseKey)
       
        if mode == .radar {
            let opacityAnimation = CABasicAnimation(keyPath: "opacity")
            opacityAnimation.fromValue = opacity
            opacityAnimation.toValue = 0.0
            opacityAnimation.autoreverses = false
            opacityAnimation.duration = frequency
            opacityAnimation.repeatCount = Float.greatestFiniteMagnitude
            animationLayer.add(opacityAnimation, forKey: radarKey)
        }
       // 这里先不让动画启动
        animationLayer.speed = 0.0
        return self
    }
 /// 恢复/启动动画
    func resumePulse() {
        let pausedTime = animationLayer.timeOffset
        animationLayer.speed = 1.0
        animationLayer.timeOffset = 0.0
        animationLayer.beginTime = 0.0
        let timeSincePauce = animationLayer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
        animationLayer.beginTime = timeSincePauce
    }
    /// 暂停动画
    func suspendPulse() {
       let pausedTime = animationLayer.convertTime(CACurrentMediaTime(), from: nil)
        animationLayer.speed = 0.0
        animationLayer.timeOffset = pausedTime
    }
    /// 移除动画
    func cancelPause() {
        layer.removeAnimation(forKey: pulseKey)
        layer.removeAnimation(forKey: radarKey)
        animationLayer.removeFromSuperlayer()
    }
}

关于swift的链式写法---非常简单,只要方法返回Self(或者对象)就行,类似与下面的写法

@discardableResult
func aaa() -> Self {
`doing some`
return self
}


最后让UIView遵守协议

extension UIView: Pulseable {}

最终调用api效果

  1. 添加并启动动画
button.addPulse(with: #colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1), mode: .radar).resumePulse()

2.暂停动画

button.suspendPulse()

3.移除动画

button.cancelPause()

相关文章

网友评论

      本文标题:swift UIView雷达动画---支持开始/暂停/移除,以及

      本文链接:https://www.haomeiwen.com/subject/jrjonttx.html