// 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))
}