코어 애니메이션은 뷰의 콘텐츠의 기하학적 특성등을 이동, 확대/축소, 회전 등을 결합하여 변형하며 애니메이팅하는데 적합하다.
아래와 같이 궤도를 도는 원의 주위를 도는 더 작은 원과, 다시 그 작은 원 주위를 도는 가장 작은 원의 움직임을 코어 애니메이션으로 묘사해보자. 이 때 그려지는 이미지는 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)
}
}