공부하다가 CALayer 라는 개념이 나와서 이를 공부해볼 겸 실습도 같이 진행해보았다.
공식 문서를 먼저 보도록 하자.
An object that manages image-based content and allows you to perform animations on that content.
이게 무슨 의미냐면,
이미지 관련 컨텐츠를 관리하며 컨텐츠에 애니메이션을 사용하도록 허락하는 객체를 말한다.
이곳에서 배경색, border, shadow 등을 설정해줄 수 있다.
일반적으로 뷰에 의해 layer 객체를 만들면 이를 layer의 delegate로 자동적으로 할당하고 이 관계를 바꿀 수 없다.
하지만 layers를 내가 스스로 만들게 되면 delegate 객체를 할당할 수 있으며 레이어의 컨텐츠를 제공하는 객체를 동적으로 사용할 수 있으며 다른 일들을 수행할 수 있다.
이것이 나오게 된 배경으로는 그래픽이 관련되어 있다.
먼저 iOS 어플에서 사용할 때 끊김없이 사용하기 위해서 초당 60 프레임 속도를 유지해야한다.
최근에는 120 프레임으로 늘어나긴 했지만 과거에는 60 프레임 속도를 유지해야했다.
여기에서 이를 유지하기 위해서 GPU에서 직접 실행되는 그래픽에 강력한 Open GL이 생겼다.
Open GL의 장점으로는 빠르게 그래픽 하드웨어에 접근할 수 있다.
단점으로는 코드의 양이 많아진다.
그래서 Core Graphics를 만들었다!!
애플은 개선에 진심이야..
이건 Low Level 작업이므로 실제 개발에서 사용하기 어려워 한다.
그래서 이를 더 간단하게 만들기 위해서 Core Animation를 만들게 되었다.
하지만 이것도 실제 앱을 만드는데 불필요한 고급 기능을 제공하고 있기 떄문에
이를 좀 더 간단하게 만들어버리자 해서 나온 것이 UIKit이다.
그래서 UIkit에서 그래픽관련 접근이 가능해진다.
이렇게 개선했기에 완벽해 보이는 UIkit에도 단점이 존재한다.
계속 부피를 줄이다보니 더 제한된 기능을 제공할수밖에 없다.
당연하죠?
그래서 우리는 UIkit를 활용해서 UIView를 그리는 것은 사실 UIkit에서 이루어지고 있는 것이 아니다.
UIView에서 실제로는 레이아웃, 터치 이벤트를 많이 처리하지만 뷰 위에 컨텐츠 그리거나 애니메이션을 넣는 행위를 직접하지 않는다.
이를 Core Animation에게 위임하여 사용하고 있다.
위임해서 하기에 가볍게 유지할 수 있겠죠?
이곳에서 화면을 그리는데 layer라는 CALayer 타입의 프로퍼티를 통해서 하고있다.
먼저 UIView는 하나의 CALayer를 가지고 있고 이 CALayer에는 여러 개의 SubLayer를 둘 수 있다.
그래서 UIView의 Subview는 UIView의 CALayer 위에 얹혀져 있다.
이 그림은 아래와 같이 보인다.
이미지 참고: 소들이 블로그
이제 이를 실제로 사용해보도록 하자!
먼저 가장 상위 Root CALayer를 만든다.
@IBOutlet weak var myVIew: UIView!
override func viewDidLoad() {
super.viewDidLoad()
myVIew.layer.backgroundColor = UIColor.blue.cgColor
myVIew.layer.borderColor = UIColor.black.cgColor
}
그 다음에 Sublayer들을 만들어서 Root CALayer에 넣어준다.
이는 마치 view에 addSubview해주는 느낌과 비슷하다.
그 코드는 아래와 같다.
func makeLayers() {
let layer1: CALayer = CALayer()
layer1.frame = .init(x: 10, y: 10, width: 30, height: 100)
layer1.backgroundColor = UIColor.red.cgColor
myVIew.layer.addSublayer(layer1)
let layer2: CALayer = CALayer()
layer2.frame = .init(x: 105, y: 10, width: 30, height: 100)
layer2.backgroundColor = UIColor.brown.cgColor
myVIew.layer.addSublayer(layer2)
let layer3: CALayer = CALayer()
layer3.frame = .init(x: 200, y: 10, width: 30, height: 100)
layer3.backgroundColor = UIColor.black.cgColor
myVIew.layer.addSublayer(layer3)
}
이 코드를 ViewDidLoad() 에서 호출해주면 아래와 같이 보여지게 된다.
UIView와 무슨차이일까?
UIView 여러개 생성하는 것보다 더 훨씬 가볍기 떄문에 나은 퍼포먼스를 기대할 수 있다고 한다.
이 말로는 납득이 안가..
그래서 더 찾아보니 그 이유가 명확했다!
먼저 UIView는 main thread에서 실행된다. 그래서 이 의미는 CPU 파워를 사용한다.
또한 더 많고 복잡한 구조를 가지고 있다.
그리고 UIView에서는 유저의 이벤트를 처리하기 떄문에 여러 Event를 받고 있다.
하지만 CALayer는 단순한 구조를 가지고 있다. 이 말은 즉, 빠르게 해결할 수 있고 스크린에 더 빠르게 그릴 수 있는 것을 의미한다.
CaLayer는 유저의 이벤트를 받지 않으므로 responder chain overhead가 존재하지 않는다.
그리고 레이어들이 GPU에서 바로 그려진다. 그래서 CPU를 사용하지 않고 분리된 thread에서 일어난다.
그러므로 CALayer가 더 가볍고 빠르게 실행될 수 있는 것이다.
하지만 여기에 많은 애니메이션을 넣는다면 성능상에 문제를 일으켜서
속도가 현저히 저하될 수 있으니 이를 조심해야한다.
그래서 이와 같은 성능 저하를 막기 위한 2가지의 프로퍼티가 존재한다.
1번쨰로는 shouldRasterize
프로퍼티가 있다
공식문서에서 의미를 찾아보면 아래와 같다
A Boolean that indicates whether the layer is rendered as a bitmap before compositing. Animatable
이 프로퍼티는 오직 한 번만 렌더링 할것인지 여부를 지정하는 속성이다.
코드를 보면 아래처럼 사용할 수 있다.
myView.layer.shouldRasterize = false
기본값은 false이다
만약 true로 한다면 처음 한번만 랜더링하고 그 이후부터 애니메이션은 재홣용된다.
만약 false라면 애니메이션 될떄마다 레이어의 모양을 다시 그린다.
이렇게 하게 되면 매번 그리기 때문에 비효율적인 성능을 보이겠죠?
그래서 이를 true 해서 성능 향상을 기대할 수 있다.
또한 레이어의 컨텐츠가 화면 주변에서 움직이기는 하나 모양은 변하지 않을 떄 사용한다.
두 번쨰로는 drawsAsynchronously
이다!
이것도 처음 보는 개념이므로 공식문서를 먼저 살펴보는 습관을 가져보자
A Boolean indicating whether drawing commands are deferred and processed asynchronously in a background thread.
이 프로퍼티는 CALayer를 그리는데 필요한 CPU작업을 background thread에서 수행해야하는지 여부를 지정하는 것이다.
myVIew.layer.drawsAsynchronously = false
false가 기본값인데 그러면 Main thread에서 수행한다.
만약 true라면 Background Thread에서 실행되겠죠?
그래서 레이어의 컨텐츠를 반복적으로 그릴 때 사용하면 성능 향상된다.
-참고사이트-
https://developer.apple.com/documentation/quartzcore/calayer/1410905-shouldrasterize
https://developer.apple.com/documentation/quartzcore/calayer/1410974-drawsasynchronously
https://developer.apple.com/documentation/quartzcore/calayer
https://babbab2.tistory.com/53