article thumbnail image
Published 2024. 3. 5. 16:16

"Core Animation"은 

iOS 혹은 OS X환경에서 Grapic Rendering 및 Animation Infra로, 

View 혹은 여러 Element들을 Animating하는데 사용한다. 

쉽게 말해 "Core Animation"은 View를 Drawing하는 Framework이다. 

"Core Graphics"도 View를 Drawing하는 API이지만, CPU에서 그리는 반면

"Core Animation"은 GPU에서 View를 Drawing한다.

 

Swift에서 일어나는 Animation의 경우,

만화처럼 Frame을 빠르게 교체해 실제로 연속적으로 일어난 것처럼 보이게 하는 것이다.

이렇게 1초에 몇 개의 Frame을 표시할 수 있는 지 "refresh rate"(주사율)로 표현하며, 단위는 Hz이다. 

iPhone의 경우 주사율은 60Hz과 120Hz가 있다. 

 

Animation의 경우 초당 60개 혹은 120개의 View를 Drawing해야 하는데, 

이를 CPU에서 진행한다면, 앱은 버벅거리게 될 것이다. 

따라서, Core Animation은 View를 GPU에서 Drawing하기 때문에, 

앱이 버벅거리는 현상 없이 랜더링이 가능하다.

그렇다 해서 Core Graphics가 필요 없는 것은 아니다.
View가 초기에 등장할 때와 같이 빠르게 랜더링 할 필요할 때 Core Graphics를 사용한다.

 

 

CALayer

CALayerCore Animation에서 가장 핵심적인 객체이다. 

 

우리는 UIKit에서 다음과 같은 코드를 볼 수 있는데,  여기서 "layer"가 CALayer이다. 

view.layer.cornerRadius = 10

 

이렇게 UIKit환경에서 Animation을 "CALayer" 객체에게 위임하게 된다. 

 

Layer-Based Drawing

앞서 말했지만, Core Animation GPU에서 "View를 Drawing"해 Animation에 적합한 Framework이다.

 

즉, 일반적으로 Layer객체를 통해 Drawing작업을 진행할 때는 단순히 Property로 조작이 가능하다. 

view.layer.cornerRadius = 10
view.layer.borderWidth = 1
view.layer.borderColor = UIColor.red.cgColor
...

 

이렇게 변경된 State Inforamtion과 Bitmap을 GPU에 전달해 Drawing 작업을 진행한다.

Bitmap은 단순 바이너리의 데이터이다. 

 

Layer-Based Animation

Core Animation은 처음 상태값과 최종 상태값만 전달하면 중간에 값 들을 Interpolation해준다.

 

앞서 말했듯, Animation은 초당 60개 혹은 120개의 View를 Drawing한다. 

View를 45도 Rotate한다고 하면,  그 사이 무수히 많은 상태를 가진 View들이 존재한다. 

해당 경우 처음 각도와 최종 각도만 전달하면,

중간값들을 가진 View들을 Core Animation에서 생성해준다.

 

 

Layer Coordinate System

Layer객체는 Content들을 Drawing하기 위해서 2가지 좌표계를 사용한다. 

  • Point-Based Coordinate : 화면 좌표에 직접 매핑되거나 다른 Layer와 관련된 위치를 지정할 때 사용
  • Unit-Based Coordinate : 화면 좌표에 바로 매핑되지 않는 경우 사용

Point-Based System에는 대표적으로 "bounds"와 "position"이 있고,

Unit-Based Coordinate에는 "anchorPoint"가 있다.

 

bounds의 경우 우리가 자주 봤었던 UIView의 bounds처럼 자신만의 좌표계와 사이즈를 정의한다.

layer에서도 frame은 존재하지만, bounds와 position으로부터 파생되는 값이기 때문에, 
bounds와 position을 사용하는 것이 좋다.

 

"anchorPoint"의 경우는 상대적인 값으로 표현해 Layer의 기준점을 정의한다.

Layer bounds의 좌측 상단을 (0, 0), 우측 하단을 (1,1)이 되고,

default 값은 (0.5, 0.5)로 Layer의 중앙이 된다. 

 

이때 "position"은 부모 Layer로부터 얼마나 "anchorPoint"가 떨어져 있는지를 정의한다. 

이때 이 anchorPoint를 기준으로 Rotation, Scaling과 같은 Transfomration에서 기준점이 된다. 

 

Anchor Point & Position

만약 다음과 같이 position을 (100, 100) 에서 (50, 50)으로 이동 시킨다고 가정해보자.

해당 상황에서는 anchorPoint는 변하지 않았고, (0.5, 0.5)로 Layer의 중앙을 가르킨다. 

하지만, position의 변경으로 상위 Layer에서 anchorPoint의 위치가 변경되었기 때문에, Layer가 이동하게 된다.

정확히 말하자면 anchorPoint는 여전히 Layer의 중심부를 가리키지만, 상대적인 값이기 때문에 따라 움직이게 된다. 

 

반대로 anchorPoint를 (0.5, 0.5)에서 (0, 0)으로 변경해보자. 

해당 경우에는 position은 그대로이고, anchorPoint만 (0, 0)으로 이동했다. 

앞서 position상위 Layer에서 anchorPoint의 위치를 정의한다는 사실을 기억해보면, 

position이 가리키던 위치(100, 100)가 Layer의 중앙이 아닌, 좌측 상단으로 변경된 것이다.

 

이렇게 position과 anchorPoint의 변경은 Layer를 이동시킨다. 

(즉, Layer의 frame이 변경된다.)

 

 

Transformation

모든 Transformation은 anchorPoint를 기준으로 수행된다.

Transformation은 다음과 같이 기존 좌표에 Transform Matrix를 곱해서 수행이 되는데, 

이 Matrix는 layer의 transform 프로퍼티를 통해 설정 및 접근이 가능하다.

var transform: CATransform3D { get set }

default값은 identity Matrix로 설정 되어 있는데, 

identity Matrix를 곱하면 아무런 변경도 일어나지 않는다. 

 

iOS에선 다음과 같이 각 Transform에 맞는 Matrix들을 제공하는데, 

Sclae의 경우, sx, sy, sz를 설정해 각 축으로 scale값을 지정할 수 있다.

// identity
CATransform3DIdentity

// x축 y축 z축으로 이동
CATransform3DMakeTranslation(tx: CGFloat, ty: CGFloat, tz: CGFloat)

// x축 y축 z축으로 Scale
CATransform3DMakeScale(sx: CGFloat, sy: CGFloat, sz: CGFloat)

// x축 y축 z축으로 Rotate
CATransform3DMakeRotation(angle: CGFloat, x: CGFloat, y: CGFloat, z: CGFloat)

// 기존의 transform을 수행 후 translate한다.
CATransform3DTranslate(t: CATransform3D, tx: CGFloat, ty: CGFloat, tz: CGFloat)

// 기존의 transform을 수행 후 Sclae한다.
CATransform3DScale(t: CATransform3D, sx: CGFloat, sy: CGFloat, sz: CGFlaot)

// 기존의 transform을 수행 후 Rotate한다.
CATransform3DRotate(t: CATransform3D, angle: CGFloat, x: CGFloat, y: CGFloat, z: CGFloat)

 

물론 다음과 같이 직접 Matrix를 구성할 수도 있다. 

view.layer.transform = CATransform3D(
	m11: 1, m12: 0, m13: 0, m14: 0,
	m21: 0, m22: 1, m23: 0, m24: 0,
	m31: 0, m32: 0, m33: 1, m34: 1,
	m41: 1, m42: 1, m43: 1, m44: 1
)

 

 

Layer-Tree

UIKit에서 UIView는 계층 구조를 이루는 것과 마찬가지로, 

CALayer도 계층구조를 이룬다. 

출처: Matt Neuburg's Programming iOS, Dive Deep into Views, View Controllers, and Frameworks

각 View의 Layer도 계층 구조를 이루지만,  별도의 Layer를 생성해 계층 구조를 구성할 수도 있다. 

여기까지보면 UIView와 굉장히 유사하지만, Layer객체는  UIResponder Chain을 형성하지 못한다. 

즉, touch와 같은 이벤트에 반응하지 못한다는 차이점이 있다. 

 

 

References

Apple Documentation

'iOS > iOS' 카테고리의 다른 글

[iOS] Custom Drop Down (2) - firstResponder 활용해 리팩토링하기  (0) 2024.03.27
[iOS] Core Animation(2) - CAAnimation  (0) 2024.03.07
[iOS] Render Loop & Hitch(2)  (1) 2024.02.24
[iOS] Render Loop & Hitch(1)  (0) 2024.02.22
[iOS] UIResponder(1)  (0) 2023.11.10
복사했습니다!