본문 바로가기
iOS

[iOS] UIView Draw Cycle

by be_beee 2022. 12. 6.

[2022.12.19] 세부 내용 리팩토링^^;;

어쩌다가 이걸 공부하게 됐나

차트 데이터를 업데이트 해야하는 상황이 생겼었는데 내가 구현한 차트 뷰는 이니셜라이징할 때만 차트 데이터를 받아서 보여주고 거기서 끝나기 때문에 문제가 발생해서 알게 되었다.

뷰를 다시 그리려면 어떻게 해야하는지에 대한 스택오버플로우 질문에 대한 답을 보면 항상 setNeedsDisplay 가 있어서 일단 그걸 사용해서 문제를 해결했었음

하지만!! 그래서 이걸 왜 써야하는데?? 다른 비슷한 이름인 메서드들도 있던데 걔네는 뭐임?? 하는 의문의 답을 찾기 위해 찾다가 UIView 드로잉 주기에 대해 공부하게 되었다.

 

UIView Drawing Concept

iOS Drawing Concepts

 

iOS Drawing Concepts

iOS Drawing Concepts High-quality graphics are an important part of your app’s user interface. Providing high-quality graphics not only makes your app look good, but it also makes your app look like a natural extension to the rest of the system. iOS prov

developer.apple.com

위의 문서를 기반으로 정리해 본 것

UIView는 컨텐츠를 표시할 때 on-demand 드로잉 모델을 사용함.

여기서 on-demand란 요청이 있을 때 공급이 있다는 느낌으로 시스템의 요청이 있어야 업데이트를 한다는 의미로 해석할 수 있을 듯하다.

다만 바로 공급(업데이트)하지는 않고, 요청들을 모았다가 적절한 주기(update cycle)에 한꺼번에 변경한다.

이것은 iOS 앱의 Main Run Loop와 관련이 있다고 함

 

Main Run Loop와 Update Cycle

⇒ Run Loop: 입력 소스를 처리하는 이벤트 처리 루프

⇒ Main Run Loop: 메인스레드가 앱이 실행될 때 자동으로 설정하고 실행하는 Run Loop

간단히 말해서 바로 업데이트 하지 않는다! 라는 말은 위의 그림과 관련 있음.

쉽게 말해서, 만약 뷰 업데이트 요청이 Main Run Loop가 진행되는 동안 발생한다면?

update cycle이 진행될 때까지 기다렸다가 한꺼번에 변경된다는 것임~!

Run Loop도 깊게 들어가면 이것저것 더 있지만… 여기선 일단 스킵! 나중에 별도로 정리합시다~

 

Drawing Cycle

View and Window Architecture

 

View and Window Architecture

View and Window Architecture Views and windows present your application’s user interface and handle the interactions with that interface. UIKit and other system frameworks provide a number of views that you can use as-is with little or no modification. Y

developer.apple.com

그러면 이 드로잉 주기는 어떤 순서로 진행되는가..는 위의 글에서 The View Drawing Cycle 섹션을 확인하면 알 수 있다. 아래는 원문을 번역하면서 순차적으로 정리해본 것.

  1. 뷰가 화면에 로드되면, 시스템이 UIView에게 draw(_:) 메서드를 통해 드로잉을 요청
  2. 시스템은 이 컨텐츠의 스냅샷을 캡쳐하고, 해당 스냅샷을 뷰의 시각적 요소(as the view’s visual representation)로 사용함. 이 스냅샷 이미지는 뷰와 관련된 모든 작업에 재사용됨.
  3. 만약 이후에 뷰의 컨텐츠가 바뀌지 않으면 draw(_:) 메서드 내의 로직은 더 이상 절.대. 호출 되지 않음.
  4. 만약 컨텐츠가 바뀐다면 개발자는 시스템에게 뷰가 업데이트 되었다고 알려줘야 함.
  5. ⇒ 처음 뷰 생성할 때 말고는 시스템이 별도로 업데이트 알림을 받아야만 드로잉 주기 순서를 재실행하는 그런 구조

 

그러면 업데이트 알림은 어떻게 보냄??

업데이트 트리거를 발생하는 방법은 이렇게 소개하고 있음

  • 부분적으로 감추던 (← 사실 이건 뭔 말인지 모르겠다) 뷰를 없애거나 옮길 때
  • isHidden 처리 되어 있던 뷰를 보이게 할 경우
  • 화면 상에 보이지 않던 뷰를 스크롤해서 보이게 할 경우나 그 반대
  • setNeedsDisplay 를 명시적으로 호출한 경우

아하 그래서 setNeedsDisplay 얘를 호출해서 리드로잉 해준 거구나~ 이해 완료.

그러면 얘가 정확히 어떤 동작을 하는지 알아봐야겠다 해서 정리한 내용⬇️⬇️⬇️

 

setNeedsDisplay

You can use this method or the setNeedsDisplay(_:)  to notify the system that your view’s contents need to be redrawn.

  • 말 그대로 네 뷰 다시 그려야함! ㅇㅇ 하고 알려줘야할 때 사용할 수 있는 메서드
  • 실제로 다음 드로잉 주기까지 그려지는 건 아니고 모든 뷰가 유효하지 않을 때 (다음 드로잉 주기에) 업데이트 된다.
  • 다시 그려! 하면 요청을 기록해두고 즉시 리턴한다.
    • 이 부분에 대해서 조금 이해가 안 됐는데, 요청을 했다는 사실은 별도로 기록해두고 메서드는 바로 종료한다라는 의미인 것 같음..?
    • 그래서 이 메서드는 비동기적으로 동작함!

여기다가 보충설명을 좀 더 하자면

  • setNeedsDisplay는 실제로 뷰를 리드로잉하는 메서드가 아님. 맨 처음에 작성한대로 네 뷰 다시 그려야함! 하고 알려주기만 하는 메서드임.
  •  setNeedsDisplay를 감지한 시스템은 바로 뷰를 업데이트하는 게 아니라 우선 현재의 런루프가 끝나기를 기다림.
    • 요 사이에 개발자는 또 다른 업데이트가 필요한 뷰가 있음을 추가로 알리거나, 새로운 서브뷰를 추가하거나 삭제하거나 리사이징하거나 등등 할 수 있음.
      • This delay gives you a chance to invalidate multiple views, add or remove views from your hierarchy, hide views, resize views, and reposition views all at once.
    • 이런 추가적인 요청사항까지 합해서 한꺼번에 업데이트 되는 것임!

 

그러면 얘네는 뭔데.. 해서 정리한 비슷해 보이는 메서드 1

displayIfNeeded

이름 비슷한 얘는 뭔데..? 싶을 수 있지만 위의 setNeedsDisplay 와 동일한 역할을 함.

  • 다만 업데이트 주기를 기다리지 않고 즉시 실행되어서, 자연스러운 애니메이션 효과처럼 뷰가 변한다고 함
  • 그리고 주기를 기다리지 않고 바로 실행되는 거니까!! 동기적으로 동작한다고 할 수 있음
  • 추가: 하지만 이 방식을 사용하는 것은 그렇게 좋지 않은 방법이라고 함.. 왜냐! 업데이트 주기를 무시해버리는 거니까!!

궁금하니까 해봐야지 → 해보려고 했는데 얘는 UIView가 아닌 CALayer 메서드라고 한다.. .충격

로직을 다 바꿔 버리기에는 두렵기 때문에 나중에 시도해보겠음…

 

그러면 얘네는 또 뭔데.. 해서 정리한 비슷해 보이는 메서드 2

  • 드로잉과 레이아웃은 개념이 살짝 다른 듯layout: 뷰의 배치 방식
  • 이런 느낌으로 이해함
  • draw: 뷰 자체를 그리는 방식

setNeedsLayout

Invalidates the current layout of the receiver and triggers a layout update during the next update cycle.

Apple Developer Documentation

 

Apple Developer Documentation

 

developer.apple.com

setNeedsDisplay에서는 draw를 알아야했듯이, setNeedsLayout에서는 layoutSubviews를 알아야 한다.

  • 그 전에 레이아웃이란? 뷰의 배치 방식ㅇㅇ 즉, 뷰의 크기와 위치를 결정하는 방식을 의미함.
  • 그럼 layoutSubviews는? 주어진 제약조건(constraints)에 따라 뷰가 갖고 있는 서브 뷰들의 크기와 위치를 결정하는 메서드
    • 얘도 draw와 마찬가지로 직접 호출하면 안 됨. 시스템만 호출하게 하고, 업데이트가 필요하다면 setNeedsLayout() 또는 layoutIfNeeded() 를 호출해야 함.

layoutIfNeeded

위의 displayIfNeeded를 보면 감이 올 수도?

얘도 역시 setNeedsLayout과 동일한 역할을 하지만 주기를 기다리지 않고 즉시 실행된다고 한다.

그리고 즉시 실행되기 때문에, 동기 호출되는 메서드라고 함. setNeedsLayout은 역시나 반대로 비동기 호출된다!

 

관련 추가 문서

Apple Developer DocumentationApple Developer Documentation

 

Apple Developer Documentation

 

developer.apple.com