iOS 类似于水波的控件及变化 Swift版

运行效果如下(带动画的,图片非gif):
iOS 类似于水波的控件及变化 Swift版_第1张图片
// MARK: 由中心向外围扩散的波纹控件
class AKRippleView: UIView {
    
    @IBInspectable public var fillColor: UIColor = UIColor.orange {
        didSet {
            self.shapeLayer.fillColor = fillColor.cgColor
            self.animShapeLayer.fillColor = fillColor.withAlphaComponent(0.8).cgColor
        }
    }
    
    public var duration: TimeInterval = 6.0 {
        didSet {
            self.animationGroup.duration = duration
        }
    }
    
    public var instanceCount: Int = 3 {
        didSet {
            self.replicatorLayer.instanceCount = instanceCount
        }
    }
    
    public var instanceDelay: Double = 2.0 {
        didSet {
            self.replicatorLayer.instanceDelay = instanceDelay
        }
    }
    
    public var path: UIBezierPath? {
        didSet {
            self.shapeLayer.path = path?.cgPath
            self.animShapeLayer.path = path?.cgPath
        }
    }
    
    public var scaleFromValue = 0.8 {
        didSet {
            let scaleAnim: CABasicAnimation = self.animationGroup.animations![1] as! CABasicAnimation
            scaleAnim.fromValue = scaleFromValue
            self.shapeLayer.transform = CATransform3DMakeScale(CGFloat(scaleFromValue), CGFloat(scaleFromValue), 0)
        }
    }
    
    public var scaleToValue = 1.3 {
        didSet {
            let scaleAnim: CABasicAnimation = self.animationGroup.animations![1] as! CABasicAnimation
            scaleAnim.toValue = scaleToValue
        }
    }
    
    // 动画
    private let animationGroup: CAAnimationGroup = {
        let opacityAnim = CAKeyframeAnimation(keyPath: "opacity")
        opacityAnim.values = [0.0, 0.7, 0.5, 0.3, 0.0]
        opacityAnim.keyTimes = [0.0, 0.1, 0.25, 0.5, 1.0]
        
        let scaleAnim = CABasicAnimation(keyPath: "transform.scale")
        scaleAnim.fromValue = 0.8
        scaleAnim.toValue = 1.3
        
        let group = CAAnimationGroup()
        group.animations = [opacityAnim, scaleAnim]
        group.fillMode = kCAFillModeBoth
        group.beginTime = CACurrentMediaTime()
        group.duration = 6
        group.autoreverses = false
        group.repeatCount = MAXFLOAT
        group.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        return group
    }()
    
    private let shapeLayer: CAShapeLayer = {
        let shape = CAShapeLayer()
        shape.fillColor = UIColor.orange.cgColor
        shape.transform = CATransform3DMakeScale(0.8, 0.8, 0.0)
        shape.allowsEdgeAntialiasing = true
        return shape
    }()
    
    private let animShapeLayer: CAShapeLayer = {
        let animShape = CAShapeLayer()
        animShape.fillColor = UIColor.orange.withAlphaComponent(0.8).cgColor
        animShape.allowsEdgeAntialiasing = true
        return animShape
    }()
    
    private let replicatorLayer: CAReplicatorLayer = {
        let replicator = CAReplicatorLayer()
        replicator.allowsEdgeAntialiasing = true
        replicator.instanceCount = 3
        replicator.instanceDelay = 2
        return replicator
    }()
    
    private var isAnimating = true
    
    convenience init() {
        self.init(frame: CGRect.zero)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        let rect = CGRect(origin: CGPoint.zero, size: frame.size)
        
        shapeLayer.frame = rect
        self.layer.addSublayer(shapeLayer)
        
        animShapeLayer.frame = rect
        replicatorLayer.frame = rect
        replicatorLayer.addSublayer(animShapeLayer)
        self.layer.insertSublayer(replicatorLayer, at: 0)
        
        NotificationCenter.default.addObserver(self, selector: #selector(startAnimation), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        let rect = CGRect(origin: CGPoint.zero, size: frame.size)
        if path == nil {
            path = UIBezierPath(ovalIn: rect)
        }
        shapeLayer.frame = rect
        animShapeLayer.frame = rect
        replicatorLayer.frame = rect
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    deinit {
        animShapeLayer.removeAllAnimations()
    }
    
    private func resumeAnimation() {
        if isAnimating {
            startAnimation()
        }
    }
    
    public func startAnimation() {
        animShapeLayer.add(animationGroup, forKey: "anim")
        isAnimating = true
    }
    
    public func stopAnimation() {
        animShapeLayer.removeAllAnimations()
        isAnimating = false
    }
}

** 使用方法 **

// 默认是oval
let rippleView = AKRippleView(frame: CGRect(x: 100, y: 100, width: 120, height: 120))
        rippleView.startAnimation()
        self.view.addSubview(rippleView)
        
        // 椭圆
        let rippleView1 = AKRippleView(frame: CGRect(x: 250, y: 100, width: 120, height: 80))
        rippleView1.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 120, height: 80))
        rippleView1.startAnimation()
        self.view.addSubview(rippleView1)
        
        
        // 弧线
        let rippleView2 = AKRippleView(frame: CGRect(x: 100, y: 300, width: 120, height: 120))
        rippleView2.path = UIBezierPath(arcCenter: CGPoint.zero, radius: 60, startAngle: CGFloat(Double.pi / 4.0), endAngle: CGFloat(Double.pi), clockwise: true)
        rippleView2.startAnimation()
        self.view.addSubview(rippleView2)

        // 带圆角的举行
        let rippleView3 = AKRippleView(frame: CGRect(x: 250, y: 300, width: 120, height: 100))
        rippleView3.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: 120, height: 120), byRoundingCorners: UIRectCorner.allCorners, cornerRadii: CGSize(width: 30, height: 30))
        rippleView3.startAnimation()
        self.view.addSubview(rippleView3)

更多用法可以自己参考UIBezierPath

也可以结合SnpaKit使用

let rippleView = AKRippleView(frame: CGRect(x: 100, y: 100, width: 120, height: 120))
rippleView.startAnimation()
self.view.addSubview(rippleView)
rippleView.snp.makeConstraints() {
           $0.centerX.equalTo(self.view)
            $0.bottom.equalTo(self.view).offset(UIDevice.iphone_568_or_less ? -60 : -75)
            $0.size.equalTo(CGSize(width: UIDevice.iphone_568_or_less ? 100 : 110, height: UIDevice.iphone_568_or_less ? 100 : 110))
        }

你可能感兴趣的:(iOS 类似于水波的控件及变化 Swift版)