Framework, CoreGraphics, CoreAnimation, CALayer

Choong Won, Seo·2024년 6월 5일

TIL

목록 보기
5/7
post-thumbnail

이 글의 시작은 UIColor와 CGColor의 차이를 찾던 어떤이로부터…. 여태껏 그랬듯 근원부터 시작한다

UIColor(User Interface), CGColor(Core Graphics).. 이런 식으로 프레임워크 이름을 앞에 붙힌 클래스들로 우리는 이것저것 만들고 사용하며 앱을 구성한다.

혹시 그럼… 당신은 프레임워크가 왜 생겨났는지 알고계신가요..?

Framework

어떤 것이 필요에 의해 생성되었을 때에는 그것이 왜 필요할 수 밖에 없었는지 생각해보면 좋은 것 같다.

우리가 현재 쓰고있는 Swift란 언어는 어떤 언어일까?

Swift는 다중 패러다임을 갖고있는 언어이며, 아래와 같은 패러다임을 차용한다.

Swift - 명령형/객체지향을 기반으로, 함수형/프로토콜 프로그래밍을 지향하는 언어

여기서 우리가 집중해야할 부분은 Swift는 객체지향언어라는 부분이다.

우리가 배웠던 객체지향 언어의 4가지 특징을 기억해보자.

  • 추상화
  • 캡슐화
  • 상속
  • 다형성

우리가 Swift를 통해 앱을 만드는 것을 떠올려보면, 앞서 말한 UIColor, CGColor등의 Class들을 가지고 기능을 추가하기도 하고, 있는 기능을 수정하기도 하고, 이미 구현되어 있는 기능을 사용하기도 한다.

이같은 개발이 가능한 이유는 객체지향언어의 추상화를 통해 Class 내부에서는 그 Class가 지켜야 하는 공통적인 속성과 행위를 정의하고, 객체(Instance)를 통해 유저에게 구현을 할 수 있게 해 놓았기 때문이다.

이 Class들을 구성하는 더 큰 틀로 정의해놓은 것이 Framework이고, 우리는 그래서 이 Framework를 통해서 Class들의 기능을 선택하고 활용하여 App을 만들 수 있다.

iOS Internal Framework

iOS내부적으로도 이런 Framework들이 4개층으로 구조화되어있다. 상위 계층으로 갈 수록 Application과 가까운 사용자 구현 기능이 들어있고, 반대로 하위 계층으로 갈 수록 Hardware(Phone), 운영체제에 가까운 기능들이 들어있다.

우리가 찾고 있었던 CGColor가 들어있는 Core Graphics Framework도 Media계층에 있는 모습을 확인할 수 있다.

Cocoa FrameworkMacOS전용 Framework로 iOS를 담당하는 Cocoa Touch와는 별개이다.

Cocoa Touch Framework의 특징은, Objective-C 런타임을 기반으로 하고, Framework안에 있는 모든 클래스는 NSObject를 상속하고있다.

(?? Objc Runtime?? 궁금해서 아주 살짝만 찾아봤는데, 구현부에서 메소드를 부르면 Method Call이 불릴 것이다. 이 Method Call을 Objc에서는 Message Sending이라고 하는데, 포인터를 통해서 부모클래스에서 Method를 찾는다. 있다면 객체에게 Message를 보내는데, 만약 Method가 없다면 superclass의 포인터를 따라서 그 부모 클래스를 또 탐색하게 된다. 이러한 체이닝을 통해서 Message를 보내는 방식으로 Runtime이 돌아간다고 한다.)

근데 여기서 UIKit의 UI(User Interface)는 알겠는데, 저 NS는 어디서 나온거란거지??

우리가 제일 자주보는 Framework는 아마 UIKitFoundation일텐데, 여기서 Foundation이 나온다.

Foundation은 UIKit에 완벽하게 포함되어있는데, Foundation에는 어떤 것들이 포함되어 있냐하면, 바로 이 NS(NextStep)함수들이 포함되어있다.

NS함수들은 Objective-C시절의 함수들을 말하고(또 너야?) 보통 글에서 Foundation이 기본 데이터타입을 포함하고 있다고 말하면, 이는 우리가 자주쓰는 String, Data 등이 아니라 NSString, NSData를 말한다.

(그래서 import Foundation을 하지 않고 String을 쓰는데 왜 돼요?? 라고 하는 경우가 있는데, 이게 사실 갖고있는 타입이 그냥 String이 아니고 NSString이어서 그렇다.)

UIColor & CGColor

그래서 사실 알고싶은건 UIColor와 CGColor였어…

myView.backgroundColor = UIColor.balck
myView.layer.borderColor = UIColor.black.cgColor

이 차이점을 앞에서 본 내용들을 토대로 해결이 가능할 것 같다!

일단 UIView, 이름만 봐도 UIKit의 class라는 것을 알 수 있다. 그럼 그 property인 backgroundColor를 보자. UIColor? 타입이므로 당연히 UIColor를 받아야하는 것을 알 수 있다.

그렇다면 두 번째의 layer, UIView의 Property로 CoreAnimation Framework라고 추측할 수 있다.

여기서 더 내려가면 borderColor, 이걸 보면 답이 나오게 된다. 타입이 CGColor?이므로 당연히 같은 CoreGraphics Framework에 있는 UIColor.black.cgColor가 쓰이게 되는 것이다!

CoreGraphicsCoreAnimation Framework는 둘 다 QuartzCore Framework에 포함되어 있다고 한다.

등장배경

잠깐 CoreGraphicsCoreAnimation의 등장배경에 대해서 알아보자.

우리가 iOS어플을 사용할 때, 화면이 끊기지 않고 부드럽게 그려지게 하기 위해서 초당 60프레임의 GPU에서 실행되는 OpenGL을 사용했었다.

하지만 OpenGL을 사용하게 되면, 단순한 작업에도 코드의 양이 방대해져서, 더 적은 코드로 그래픽을 구현할 수 있는 Core Graphics라는 것을 만들었다. (CGColor, CGRect등이 있는 Framework)

하지만 이마저도 조금은 Low level작업이라서, 더 간단하게 사용할 수 있는 Core Animation Framework를 또 만들게 되었다.

그러다가 또, 이 작업들 중에서 가장 필요한 것들만 모아서 가장 High Level에서 그래픽에 접근을 할 수 있게 만든 Framework가 바로 UIKit이다!!

그래서 우리는 UIKit을 통해서 쉽고 간편하게 화면을 그릴 수 있지만, 사실 내부적으로 보면 컨텐츠나 애니메이션을 그리는 행위는 UIKit이 하는 것이 아닌, Core Animation에게 위임(delegate)해서 처리하는데, 이 것이 바로 CALayer타입의 프로퍼티인 layer을 통해서 하는 것이었다(!)

아하! 그래서 앞에 myView.layer.borderColor같은 코드에서 borderColor에 접근할 때, 항상 layer을 거쳐서 접근했는데 이것이 borderColor그리는 행위를 layer(Core Animation)에게 위임했기 때문에 이렇게 접근을 하는 것이었구나 !!

CALayer

그러면 이제 앞서 알아본 CALayer에 관해서 알아봐야겠지?

Shadow를 그려주고, Border, Corner를 설정해주는 것은 알겠는데, 내부적으로 어떻게 설정되어있길래 이런 역할을 하는지 궁금해진다.

먼저, 누가 자꾸 이 View에 layer를 넣어주는 것일까? 나는 layer를 넣은 적이 없는데, 모든 View에서 layer에 접근이 가능하다. → 답은 layerClass라는 Type Property이다.

layerClass

layerClass는 기본값으로 우리가 알고있는 CALayer를 반환해주고, CALayer가 아니라 다른 layer를 반환해주고 싶다면 이 프로퍼티를 override하면 된다.

엥?? layer가 CALayer말고 다른 타입도 있어? → YES, 심지어 우리가 본 적도 있다!!

우리가 MapView같은 큰 리소스를 사용하는 View를 그릴 때, 타일처럼 네모칸으로 나누어져서 로딩되는 것을 본 적이 있을 것이다. 이 때 사용되는 것이 CATiledLayer이다.

CALayer

그렇다면 기본적인 이 CALayer는 어떤 기능을 담당하는 것일까?

공식문서를 살펴보면 알 수 있는 것은, 먼저 layer는 주로 View의 Backing Store을 제공하는 용도로 사용된다고 한다. 예를 들면, View의 위치, 사이즈, 변형 등의 정보들을 보관한다. → View의 관리자

이와 동시에 스스로 시각적인 속성들을 가질 수도 있는데(View가 없이도 Contents를 표시하기 위해 사용되기도 한다.), 우리가 잘 알고있는 Shadow, Border, Corner같은 속성들이 여기에 속할 것 같다.

또, layer는 delegate를 사용하는데, View에 의해서 자동으로 생성된 layer는 자신의 View를 대리자로 자동 설정한다. 여기서, 개발자는 이 관계를 절대 변경해서는 안된다고 한다.

앞문장을 살펴보면 View를 통해서가 아니라 직접 layer가 만들어질 수도 있다는 것 같은데(우리가 addSublayer() 등 했던 것처럼이라고 생각했는데 좀 더 살펴보니 View에 rootLayer는 하나고 여러개의 Sublayer를 가진다고 한다.) 이 경우에는 delegate에 원하는 객체를 할당해서 View-Layer관계를 임의로 설정해줄 수 있다고 한다.

Core Animation

어떤 관계로 이어져있는지는 알았지만, 그래서 유용하구나! 하고 와닿지는 않는 것 같다. Core Animation파트에서 좀 더 알아보자.

Animation을 만드는 과정을 참고하면 layer의 가장 중요한 역할을 알 수 있다.

Animation을 만들 때, layer는 View가 가지고 있는 Contents를 bitmap으로 바꿔서 GPU에 전달하고, Animation완료된 새로운 bitmap을 다시 GPU에게 받아서 View를 그린다.

→ 아하!! bitmap을 이용해서 빠른 이미지 연산을 하고, 그 값을 Backing Store에 저장하는구나!

맞다. View에서 직접 Contents를 그리게되면 MainThread에서 draw로 처리하기 때문에 비용이 크지만, layer는 GPU를 사용하기 때문에 비용이 크지 않다. (ㄷㄷ 똑똑하게 돌아간다…)

이 역할을 해주는 것이 CALayerDelegate고, 이를 채택한 View가 display(layer:), draw(layer: inContext:)함수를 구현해서 자신의 bitmap을 저장하게 할 수 있다.

(View에 포함되어있는 layer의 경우에는 자동적으로 되어있다고 하네요.)

실제로 관찰해보면 이 프로토콜을 채택하고 있는 모습을 확인할 수 있다!

View Programming Guide에서 실제로 각 View들을 살펴보면, 자신과 대응되는 layer를 하나씩 가지고 있는 모습을 살펴볼 수 있다.

그래서 실제로 View에서 화면을 그리는 코드는 최대한 적게 호출된다고 한다! (layer의 bitmap을 사용하는 것이 더 효율적이니까.)

동작방식

  1. Contents에 변경이 있거나 처음 그려질 때 View에서 Contents를 그리고,

  2. delegate를 통해서 CALayer에 Contents를 전달한다.

  3. CALayer는 이 Contents를 bitmap으로 캐싱해두고 있다가 필요할 때 사용한다.

와우 정리하면서 엄청나게 많은 애매했던 개념들이 정리된 느낌이다. 왜이리 iOS에서 화면도 부드럽고 Native가 주는 느낌이 다르다고 생각한 적이 종종 있는데, 당연히 OS와 Animation의 차이도 있겠지만, 이런 layer기반 Contents운용 방식도 한 몫 하는 것 같다. Framework부분도 명확하게 정리한 것 같아서 뿌듯하다. 다음에 또 보자!

참조

[iOS/Swift] 프레임워크(Framework)와 클래스(Class)와의 관계

객체 지향 프로그래밍의 4가지 특징ㅣ추상화, 상속, 다형성, 캡슐화 -

[Objective-C] Runtime, Structure

[iOS] view.layer의 정체를 찾아서

iOS) CALayer 제대로 이해하기

profile
UXUI Design Based IOS Developer

0개의 댓글