본문 바로가기
iOS/예제

[iOS 예제] Recording Wave View 만들기

by Sky Titan 2022. 7. 2.
728x90

 이 애니메이션 효과를 뭐라고 표현해야할지 모르겠는데 주로 녹화같은 것 할 때 깜빡거리는 애니메이션 용도로 자주 봤던 것 같아서 Recording Wave View라고 했다.

 

Github: https://github.com/Sky-Titan/WaveViewExample

 

GitHub - Sky-Titan/WaveViewExample: WaveView by Swift Example

WaveView by Swift Example. Contribute to Sky-Titan/WaveViewExample development by creating an account on GitHub.

github.com

 

 

WaveView

import UIKit

class WaveView: UIView {
    override class var layerClass: AnyClass {
        WaveLayer.self
    }
    
    var waveLayer: WaveLayer? {
        layer as? WaveLayer
    }
    
    @IBInspectable
    var waveColor: UIColor? {
        get {
            waveLayer?.waveColor
        }
        
        set {
            waveLayer?.waveColor = newValue
            waveLayer?.setNeedsDisplay()
        }
    }
    
    @IBInspectable
    var circleColor: UIColor? {
        get {
            waveLayer?.circleColor
        }
        
        set {
            waveLayer?.circleColor = newValue
            waveLayer?.setNeedsDisplay()
        }
    }
    
    @IBInspectable
    var waveDuration: CGFloat {
        get {
            waveLayer?.waveDuration ?? 0.3
        }
        
        set {
            waveLayer?.waveDuration = newValue
            waveLayer?.outerLayer?.removeAllAnimations()
        }
    }
    
    func startWave() {
        waveLayer?.startWave()
    }
    
    func stopWave() {
        waveLayer?.stopWave()
    }
}


class WaveLayer: CALayer {
    fileprivate var outerLayer: CAShapeLayer?
    
    fileprivate var waveColor: UIColor?
    fileprivate var circleColor: UIColor?
    fileprivate var waveDuration: CGFloat = 0.3
    
    override func draw(in ctx: CGContext) {
        let radius: CGFloat = min(self.bounds.width / 2, self.bounds.height / 2)
        let center: CGPoint = CGPoint(x: self.bounds.width / 2, y: self.bounds.height / 2)
        
        self.removeAllSubLayers()
        
        let outerLayer = makeCircleLayer(center, radius: radius, fillColor: waveColor?.cgColor)
        addSublayer(outerLayer)
        self.outerLayer = outerLayer
        
        let innerLayer = makeCircleLayer(center, radius: radius, fillColor: circleColor?.cgColor)
        addSublayer(innerLayer)
    }
    
    private func makeCircleLayer(_ center: CGPoint, radius: CGFloat, fillColor: CGColor?) -> CAShapeLayer {
        let circleLayer = CAShapeLayer()
        circleLayer.frame = self.bounds
        circleLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
        circleLayer.position = CGPoint(x: self.bounds.width / 2, y: self.bounds.height / 2)
        circleLayer.path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: .pi * 2, clockwise: false).cgPath
        circleLayer.fillColor = fillColor
        return circleLayer
    }
    
    fileprivate func startWave() {
        outerLayer?.removeAllAnimations()
        
        let animation = CABasicAnimation(keyPath: "transform.scale")
        animation.fromValue = 1
        animation.toValue = 1.3
        animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
        animation.repeatCount = .infinity
        animation.autoreverses = true
        animation.duration = waveDuration
        animation.beginTime = CACurrentMediaTime()
        
        outerLayer?.add(animation, forKey: "wave")
    }
    
    fileprivate func stopWave() {
        outerLayer?.removeAllAnimations()
    }
}
extension CALayer {
    func removeAllSubLayers() {
        self.sublayers?.forEach({
            $0.removeFromSuperlayer()
        })
    }
}
  • innerLayer, outerLayer라고 하는 원형 subLayer 2개를 사용
  • outerLayer에 scale 조절하는 easeInOut 타이밍의 애니메이션을 넣음으로써 wave 애니메이션을 표현한다.
728x90

댓글