[iOS] Update Cycle

도윤·2022년 10월 17일
0

iOS

목록 보기
8/11

setNeedsLayout과 관련된 내용을 학습하다가 Runloop, UpdateCycle을 알게 되었고 Display, Layout, Constraint 업데이트와 관련된 내용이 있다는 점을 알게 되어 이에 기반한 내용을 추가적으로 작성합니다!

혹시 이 전 게시글의 내용이 궁금하시다면 지난 게시글을 한번 보셨으면 좋겠습니다!

간략하게 요약하면 Update Cycle이 발생할 때 크게 layout, display, constraint가 업데이트가 됩니다.

이렇게 UIView와 관련된 것들이 어떻게 코드에서 어떻게 작동하는지 더 알아보겠습니다!

공식 문서와 부족한 내용은 https://tech.gc.com/demystifying-ios-layout/ 를 참고하여 게시글을 작성하였습니다.

UIView에는 몇가지 책임이 있다고 합니다.

Drawing and animation

  • UIKit이나 Core Graphichs를 사용하여 사각형에 그들의 컨텐츠를 그린다.
  • 몇가지 속성을 이용하여 애니메이션화 시킬 수 있다.

Layout and subview management

  • subView들을 가질 수도 있고 아예 없을 수도 있다.
  • 뷰의 subviews들의 위치와 크기를 조정
  • 뷰의 계층의 변화에 따라 View의 크기와 위치를 자동으로 재정의할 수 있다.

Event Handling

  • View는 UIResponder의 subclass이고 터치나 다른 이벤트에 대하여 반응한다.
  • 공통의 gesture를 처리할 수 있는 gesture recognizer를 설치할 수 있다.

Draw & animation은 Display, Layout and Subview Management는 Layout, Constraint를 다루는 느낌이 듭니다!

Display


Draw views

View Drawing은 필요하면 수행된다. 당연한 소리같습니다. 처음 뷰가 보여질때나 layout의 변화로 인해 일부분이라도 보여지면 시스템이 컨텐츠를 그리라는 명령을 합니다. UIKit이나 Core Graphics같은 custom contents를 담당하는 내용을 포함하는 View는 draw(_:)메소드를 호출하여 컨텐츠를 그립니다. 이 메소드를 호출하기 전에 시스템에서 자동적으로 설정(set)이 되어 있고 view의 컨텐츠를 그리는 책임이 있다!

이 메서드는 뷰가 처음 보여질때나 이벤트가 발생해 보여지는 뷰가 invalidate될 때 호출되는데, 직접 호출하면 절대 안된다고 합니다!대신에 setNeedsDisplay(_:), setNeedsDisplay()를 호출해야 된다!

두 메서드의 차이점은 전체를 다시 그릴지, 일부의 Rectangle을 다시 그릴지를 설정하는 차이입니다.

setNeedsDisplay()

시스템에 뷰의 컨텐츠를 redraw를 해달라는 메서드입니다!
이 메서드를 실행하면 요청 후 즉시 return됩니다. Update Cycle을 보셨다면 아시겠지만 다음 Update Cycle에 실행됩니다!

이 메서드는 View의 Display(BackgroundColor, Tintcolor, UIImage)와 같은 redraw에만 사용해야 된다. 간단한 위치나 크기의 변화는 redraw되지 않는다!

전자의 setNeedsDisplay(_ rect: CGRect)의 경우 뷰를 그리는 동작 방식은 같으나 전체 View가 아닌 특정 지역만 redraw합니다.

뷰의 UI 컴포넌트를 업데이트 하는 것은 View의 dirty flag를 활성화 시켜서 명시적으로 setNeedsDisplay를 호출하지 않아도 다음 Update Cycle에 뷰를 다시 그려지도록 유도합니다. 하지만 UI컴포넌트와 직접 관련이 없지만 Update Cycle에 뷰를 업데이트하고 싶다면 명시적으로 setNeedsDisplay를 호출해야 합니다.

이전 게시글에서도 언급했지만 Update Cycle의 간격은 1/60초로 사용자가 느끼기엔 매우 빠른시간입니다!

displayIfneeded

display를 redraw한다는 점에서 유사한 점이 있지만 다시 그리는 시점에서 차이가 발생합니다!
setNeedsDisplay()를 호출하게 되면 즉시 업데이트되는 것이 아닌 다음 Update Cycle에 display가 업데이트되는 것과 다르게 호출 즉시 update process를 시작합니다.

필요시에 업데이트를 즉시 하는것입니다. 공식문서에는 setNeedsDisplay()를 호출하여 다음 사이클동안 업데이트하는 것을 권장하네요!

Layout


이부분은 공식문서의 내용보다 https://tech.gc.com/demystifying-ios-layout/ 이 더 잘나와 있어서 번역을 했습니다!

layoutSubviews()

UIView와 자식들의 위치와 크기(layout)을 재조정합니다. 현재 뷰 뿐만 아니라 subViews들의 위치와 사이즈도 조정한다. 이 메서드는 뷰의 모든 서브뷰들의 layoutSubviews()들을 재귀적으로 호출하기 때문에 부하가 매우 크다. view의 frame을 다시 계산할 때 이 메서드가 호출되기 때문에 layoutSubviews를 오버라이드해서 frame의 크기와 사이즈를 조절할 수 있다. 그러나 layoutSubviews를 직접 호출해서는 안된다. layoutSubviews를 시스템이 호출하도록 유도하는 여러 개의 방식들이 존재한다. 이러한 것들은 모두 runloop이 돌아가는 동안 layoutSubviews가 실행되는 시점이 다르며 layoutSubviews를 직접 호출하는 것 보다 부하가 덜 하다.

layoutSubviews가 완료될 때, viewDidLayoutSubviews가 View를 소유한 ViewController에서 호출된다. layoutSubviews는 View의 layout이 변화했다는 유일한 콜백이므로 레이아웃과 관련된 로직을 viewDidLayoutSubviews에 호출해야 합니다. 이것이 오래된 레이아웃이나 위치 변수를 다른 계산에 사용하는 실수를 막는 유일한 방법이다.

앞서 말씀드린 layoutSubviews대신에 호출해야 하는 메서드를 호출할 필요 없이 자동으로 refresh trigger가 발생하는 경우가 있다.

  • view resize
  • adding Subview
  • UIScrollView에서 유저가 scroll, 이때 UIScrollView와 부모 뷰에서 layoutSubviews가 호출
  • 디바이스 회전
  • View의 Constraint 변경

위의 방법들은 시스템이 View의 위치가 변했고, 다시 계산되도록 layoutSubviews를 호출되게 된다..

이외에 직접 호출하는 방법들이 2가지가 존재.

setNeedsLayout

앞서 setNeedsDisplay()와 동작 방식이 유사합니다!
setNeedsDisplay() 호출되면 즉시 리턴되고 View를 즉시 업데이트 하는 것이 아니라 다음 Update Cycle에 Layout을 업데이트 합니다.

layoutIfNeeded

이 메서드는 displayIfNeeded와 유사합니다. 다음 Update Cycle에 View의 layout을 재정의하는 것 아니라 메서드 호출 즉시 Layout을 재정의합니다.

하지만 무조건 호출되는 것은 아니다! view가 refresh되어야 함을 감지해야 한다.

  • Update flag이 켜져있고 layoutIfNeeded()
  • setNeedsLayout() -> layoutIfNeeded()
  • update flag/setNeedsLayout() -> layoutIfNeeded() -> layoutIfNeeds()
    세번째 경우는 앞서 모두 layoutSuvviews가 호출되어 layout이 재지정 되었기 때문에 업데이트할 layout이 없다. 따라서 layoutIfNeeds()를 호출해도 호출되지 않는다.

layoutIfNeeded는 Constraints를 애니메이션하는 상황에 유용합니다! 애니메이션을 시작하기 전 layoutIfNeeded를 호출하여 모든 레이아웃 업데이트가 애니메이션 전에 수행되도록 전파할 수 있습니다.

Constraints

updateConstraints

이 메서드는 Auto layout을 이용하는 View의 Constraints를 동적으로 변경할 때 사용될 수 있다.
Layout의 layoutSubviews()Display의 draw()와 같이 updateConstraints는 오직 오버라이딩되어야 하며 명시적으로 호출하면 안된다.
정적인 Constraints에 대하여 Interface Builder나 View의 생성자나 ViewDidLoad에서 정의되어야 한다.

일반적으로 Constraints를 활성화/비활성화하거나 Constraint의 우선 순위나 Constant를 변경하거나 View의 계층에서 삭제하는 것은 updateContraints를 다음 Update Cycle에서 호출하게 합니다. 이 또한 명시적으로 호출하는 방법이 존재합니다!

setNeedsUpdateConstraints

setNeedsUpdateConstraints를 호출하는 것은 다음 Update Cycle에서 Constraint가 업데이트 되도록 한다.
앞서 보신 setNeedsDisplay()setNeedsLayout()와 비슷하게 동작합니다.

updateConstraintsIfNeeded

익숙한 IfNeeds 키워드가 보이죠? 이 역시 유사합니다!
하지만 Auto Layout을 사용하는 뷰에만 유효합니다.


요약!

Layout

  • layoutSubviews(): view와 그 subView들의 크기나 위치를 다시 지정하는 것. Frame의 변화나 크기를 다시 필요해야 할 필요가 있다고 판단이 되면 시스템이 알아서 호출. 직접 호출 X.
  • setNeedsLayout(): 직접 명시적으로 업데이트 호출. 즉시 되는 것이 아닌 다음 update Cycle에 적용. 가장 부하가 적은 메서드
  • layoutIfNeeds(): 이 또한 직접 명시적으로 호출. 호출 즉시 layout이 업데이트. 애니메이션 효과같은 곳에 적합.

Draw

  • Draw(): layoutSubViews와 동작은 유사하지만 SubView들의 draw는 호출하지 않는다. bounds나 text, backgroundColor등이 업데이트 될 때 호출된다. (update Flag가 On)
  • setNeedsDisplay(): 직접 명시적으로 Display 업데이트 호출. 다음 Update Cycle에 적용. 가장 부하가 적은 메서드
  • displayIfNeeded(): 이 또한 직접 명시적으로 호출. 호출 즉시 Display 업데이트

Constraints:

  • updateConstraints(): AutoLayout를 사용하는 View가 동적으로 변화하는 Constraints를 적용. 직접 호출 X, view가 계층에서 삭제되거나 Constraint 활성화/비활성화, constraints의 우선순위나 Constant가 변경될때 자동으로 호출
  • setNeedsUpdateConstraints(): 직접 명시적으로 Constraints 업데이트 호출. 다음 Update Cycle에 적용. 가장 부하가 적은 메서드
  • updateConstraintsIfNeeded(): 이 또한 직접 명시적으로 호출. 호출 즉시 Constraints 업데이트.

0개의 댓글