코어애니메이션 시작하기

코어 애니메이션은 뷰의 콘텐츠의 기하학적 특성등을 이동, 확대/축소, 회전 등을 결합하여 변형하며 애니메이팅하는데 적합하다.

아래와 같이 궤도를 도는 원의 주위를 도는 더 작은 원과, 다시 그 작은 원 주위를 도는 가장 작은 원의 움직임을 코어 애니메이션으로 묘사해보자. 이 때 그려지는 이미지는 CAShapeLayer를 사용해서 패스를 통해 그릴 수도 있겠지만, 간단한 원이기 때문에 레이어의 모서리를 둥글려서 표현할 것이다.

그려지는 모든 궤도는 테두리로 그려지는 원과 원 위의 작은 원으로 구성된다. 그러한 궤도를 CALayer로 만들어주는 함수 orbit(with: color:)를 다음과 같이 작성할 수 있다. 기본적으로 궤도를 표현하는 레이어가 있고, 다시 궤도 위의 위성을 표현하는 레이어를 만든 다음, 위성 레이어를 궤도 레이어에 서브 레이어로 추가해주면 된다.

func orbit(with diameter: CGFloat, color: NSColor) -> CALayer {

    // 궤도를 표현하는 원
    let _orbit = CALayer()
    _orbit.bounds = CGRect(x: 0, y: 0, width: diameter, height: diameter)
    _orbit.cornerRadius = diameter / 2
    _orbit.borderColor = color.cgColor
    _orbit.borderWidth = 1.5
    
    // 궤도 상의 위성을 표현하는 원
    let planet = CALayer()
    let r = diameter / 10
    planet.bounds = CGRect(x: 0, y: 0, width: r, height: r)
    planet.borderWidth = 1.5
    planet.cornerRadius = r/2
    planet.backgroundColor = color.cgColor
    planet.borderColor = color.cgColor
    planet.borderWidth = 1.5
    planet.borderColor = color.cgColor
    // 상위 레이어의 원점을 기준으로
    // 6시 방향에 위치하도록
    // X는 중간, Y는 0 
    planet.position = CGPoint(x: diameter/2, y: 0)
    
    _orbit.addSublayer(planet)
    return _orbit
}

다음 코드는 애니메이션 객체를 만든다. 무한 회전을 반복하면 되며, 이 때 변할 수 있는 값은 회전속도 즉, 지속시간이다 한 바퀴 도는데 얼마나 걸리는지를 정해서 넘겨주면 그에 맞는 애니메이션을 생성하는 함수 animation(with:) 를 다음과 같이 작성할 수 있다.

회전하는 애니메이션은 CABasicAnimation으로 만들어지는데, 애니메이션이 추가될 레이어의 transform.rotation 키패스의 속성값을 조작하게 될 것이다.

참고로 회전하는 동작 자체에는 가속이나 감속이 적용되지 않을 예정이므로 애니메이션의 타이밍 함수를 선형으로 지정해줄 필요가 있다.

func animation(with duration: Double) -> CABasicAnimation {
    let anim = CABasicAnimation(keyPath: "transform.rotation")
    anim.timingFunction = CAMediaTimingFunction(name: .linear)
    anim.fromValue = 0
    anim.toValue = 2 * Double.pi // 2pi = 360'
    anim.duration = duration
    anim.repeatCount = HUGE
    return anim
}

이제 회전하는 레이어를 만드는 것은 간단하다. 레이어의 add(_:forKey:) 메소드를 호출하여 애니메이션을 추가한다. 이 때의 키는 애니메이션을 구분하기 위한 용도이며, 레이어의 특정한 키패스를 가리키는 항목이 아니다.

이제 세 개의 궤도를 만들고 각각을 연결한 후 애니메이션을 달아보자.

class ViewController: NSViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.wantsLayer = true
        view.layer?.backgroundColor = NSColor.white.cgColor
        
        let orbit1 = orbit(with: 200, color: .red)
        let orbit2 = orbit(with:120, color: .blue)
        let orbit3 = orbit(with:30, color: .green)
        orbit1.position = CGPoint(x:view.frame.midX, y: view.frame.midY)
        orbit2.position = CGPoint(x: 100, y:0)
        orbit3.position = CGPoint(x: 60, y: 0)
        
        orbit2.addSublayer(orbit3)
        orbit1.addSublayer(orbit2)
        
        orbit1.add(animation(with: 5.0), forKey: "orbit1")
        orbit2.add(animation(with: 3.5), forKey: "orbit2")
        orbit3.add(animation(with: 1.1), forKey: "orbit3")

        
        view.layer?.addSublayer(orbit1)
        
    }
}