[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

🍎 Apple/UIKit

[UIKit] View Drawing Cycle

inu 2022. 3. 16. 21:22
반응형

참고

View의 Drawing Cycle이란 View가 로드되거나 변경이 생겼을 때 이를 화면에 적용시켜 그리는 과정을 뜻합니다. 이들은 즉각적으로 처리되지 않고, Main Run loop에 의존적인 형태를 가집니다. 

Main Run loop

https://developer.apple.com/library/content/documentation/General/Conceptual/Devpedia-CocoaApp/MainEventLoop.html

iOS의 이벤트는 각 스레드마다 존재하는 Run loop를 통해 처리됩니다. 발생하는 이벤트들이 모두 시스템상의 이벤트큐에 들어가고, Run loop가 돌아가면서 이들을 하나씩 가져와 처리하는 방식입니다.

 

그 중에서도 Main Run loop는 사용자의 입력 이벤트를 처리하고 적절한 응답을 트리거하는 역할을 수행합니다.

Update Cycle

Main Run loop의 마지막 단계에서는 Update Cycle이라는 것이 발생합니다. 이 Update Cycle에서 시스템은 Layout(Size, Position), Display(Color, text, image,...), Constraints(Auto Layout에 필요한 제약조건)를 업데이트하는 작업을 수행합니다.

 

이벤트들을 처리하는 과정에서 View의 변경이 요청되면 시스템은 이를 바로 처리하는 것이 아니라, 이를 체크(마킹)해 놓습니다. 그리고 다음에 발생하는 Update Cycle에서 체크된 요청들을 확인해서 실질적 변화를 수행하는 것입니다.

draw, layoutSubviews, updateConstraints

Update Cycle 과정에서 실질적으로 변화에 대한 처리를 수행하는 것은 View 내부의 메서드입니다.

 

Display의 경우 draw, Layout의 경우 layoutSubviews, Constraints의 경우 updateConstraints가 그 처리를 담당합니다. 이들은 최초로 View가 load 되어 화면에 나타날 때도 불려집니다. (최초로 load되어 그려질때도 당연히 이들 과정이 필요하기 때문입니다)

 

아래 그림을 봅시다.

https://green1229.tistory.com/67

  1. Constrains 값을 기반으로 초기 설정된 Layout(Size, Position)을 변경 혹은 유지
  2. Layout 값을 기반으로 Size 및 Position 설정
  3. 최종 Drawing 처리

순서로 이루어짐을 알 수 있습니다. 이를 통해 만약 Constrains을 변경하고 updateConstraints가 호출되면 자연스럽게 Layout이 변경되면서 layoutSubviews가 불리고 최종적으로 Drawing까지 수행될 것을 예상할 수 있습니다. 아래 그림은 그 과정을 도식화하고 있습니다.

https://tech.gc.com/demystifying-ios-layout/

setNeedsUpdateConstraints, setNeedsLayout, setNeedsDisplay

하지만 애플은 updateConstraints, layoutSubviews, draw는 Update Cycle에서 수행되는 것이 더 바람직하니까 직접 사용하지 말라고 말합니다. (아마 Update Cycle에서 한 번에 처리하는 것이 아니라 사용자가 직접 부르도록 하면 동시에 발생하는 다른 이벤트 등으로 인해 Update 처리가 제대로 수행되지 않을 것을 우려해서인 것 같네요. 또 동시에 적용하지 않으면 고비용의 작업을 여러번 실행하면서 효율성이 떨어지는 문제도 있겠죠.)

 

그래서 존재하는 것이 setNeedsUpdateConstraints, setNeedsLayout, setNeedsDisplay 같은 업데이트 요청 메서드들입니다. 이들은 각각 Constraints, Layout, Display에 대한 업데이트를 명시적으로 예약하는 역할을 합니다.

이들이 호출되면 시스템은 이를 체크해뒀다가 다음 Update Cycle 단계에서 updateConstraints, layoutSubviews, draw를 호출합니다.

 

물론 직접적으로 setNeedsUpdateConstraints, setNeedsLayout, setNeedsDisplay와 같은 메서드를 호출하지 않아도

  • View의 크기 변경
  • SubView 추가
  • ScrollView에서 Scroll 발생
  • 디바이스 회전으로 인한 화면표시 모드변경 (가로, 세로)
  • View의 Constraints 변경

과 같은 상황에서는 setNeedsLayout, setNeedsDisplay가 자동으로 트리거됩니다. 그래서 지금까지 setNeedsLayout, setNeedsDisplay를 직접적으로 호출하지 않아도 UI 업데이트가 수행되었던 것이죠.

updateConstraintsIfNeeded, layoutIfNeeded

사실 Update Cycle을 기다리지 않고 바로 View를 업데이트할 수 있는 방법이 있습니다. 바로 updateConstraintsIfNeeded 혹은 layoutIfNeeded 메서드를 호출하는 것입니다. 해당 메서드를 사용하면 Update Cycle와 관계없이 각각 곧바로 updateConstraints, layoutSubviews를 실행시킵니다.

 

특히 layoutIfNeeded는 Contraints를 통해 애니메이션을 구성하고 싶을 때 유용하게 사용할 수 있습니다. 단순히 Contsraint를 변경하는 애니메이션을 만들 경우 결국 이에 대한 처리는 Update Cycle에서 처리되기 때문에 애니메이션 효과를 줄 수 없습니다. 하지만 Constraints 변경후 layoutIfNeeded 메서드를 애니메이션 코드에 추가하면 layoutIfNeeded가 순차적으로 처리되면서 애니메이션을 확인할 수 있게 됩니다.

Summary

마지막으로 이들 내용을 정리하는 표를 첨부하고 게시글 마치겠습니다.

마크다운으로 변환하면서 표가 자꾸 깨져서 이미지로 첨부... 티스토리 개발자님들 마크다운 개선좀 해주세요 🥲

 

반응형