iOS 개발을 시작하고 얼마 지나지 않아 setNeedLayout과 layoutIfNeeded라는 것을 접하게 되었고 그 부분에 대해서 정리를 하게 되었습니다.
우선 위 2개를 비교하기전에 Main Run Loop를 알아 야합니다. 그에 대한 내용은 여기에서 확인 하면 됩니다.
이 main Run Loop는 사용자가 발생시킨 이벤트를 처리하고 다시 main Run Lopp로 권한이 돌아오는 시점을 update Cycle이라고 합니다.
Update Cycle
Update Cycle을 간단하게 설명하면 layout을 재배치 하거나 view들을 다시 그리는 작업을 진행하는 구간이라 생각하시면 됩니다.
main run loop에서 이벤트가 처리되는 과정에서 애니메이션이 동작하거나 크기 및 위치가 변경될 수 도있습니다. 시스템은 이러한 크기나 위치가 변화되는 View를 체크합니다. 그리고 모든 핸들러가 종료되고 main run loop로 권한이 다시 돌아오는 시점인 update cycle에서 이런 View들의 값을 바꿔주어 View의 크기나 위치 변화를 적용시킵니다.
다시말해 크기나 위치 값을 변경하는 코드와 실제로 변경된 값이 반영되는 시점에는 시간차가 존재한다는 뜻입니다. 이런 시간차는 사람이 체감하기 힘듭니다. 그러나 이런 갱신 주기가 되어야 View의 크기나 위치가 변경됩니다. 하지만 즉각 변경 하고 싶다면 어떻게 해야 할까요?
그럴때 필요한 것이 setNeedLayout 과 layoutIfNeeded 입니다.
layoutSubviews
자꾸 두 메소드를 설명하기 전에 다른 메소드를 설명하게 되지만 다 필요하니 설명을 하겠습니다. 위 setNeedLayout 과 layoutIfNeeded 가 호출되면 공통으로 View를 갱신하는 메소드인 layoutSubviews가호출 됩니다.
layoutSubviews는 View의 값을 호출한 즉시 변경시켜주는 메소드입니다. 호출되면 해당 View의 모든 Subview들의 layoutSubViews() 또한 연달아 호출합니다. 그렇기 때문에 비용이 많이 드는 메소드이고 그렇기 때문에 직접 호출하는 것은 지양됩니다. 그래서인지는 모르겠지만 공식문서에서는 이 함수를 직접 호출 할 수 없고, 시스템에 의해서 View의 값이 재계산되어야 하는 적절한 시점(update cycle)에 자동으로 호출됩니다.
그렇기 때문에 layoutSubViews를 유도할 수 있는 여러 방법이 존재합니다. 이는 일종의 update cycle에서 layoutSubViews의 호출을 예약하는 행위라고 할 수 있습니다.
UIViewController내의 View가 재계산되어 다시 그려지는 행위가 발생하면, 즉 layoutSubViews가 호출되고 View의 값이 갱신되고나면 뒤이어 UIViewController의 메소드인 viewDidLayoutSubviews가 호출됩니다. 그렇기 때문에 갱신된 View 값에 의존하는 행위들은 viewDidLayoutSubviews에 명시를 해주어야 합니다.
위에서 언급한 것처럼 layoutSubviews를 update cycle에서 호출되게끔 자동으로 예약을 해주는 상황들이 몇 가지 존재합니다. 즉 다음 상황에서는 시스템이 자동으로 size와 position이 변경되어야 하는 View라고 체크를 하고 update cycle에서는 layoutSubviews가 호출되어 체크된 View의 layer와 position에 변경된 값을 반영합니다.
- View의 크기를 조절할 때
- Subview를 추가할 때
- 사용자가 UIScrollView를 스크롤할 때
- 디바이스를 회전시켰을 때 (Portrait, Landscape)
- View의 Auto Layout contraint 값을 변경시켰을 때
위에 나열된 시점에는 자동으로 update cycle에서 layoutSubviews를 호출하는 행위를 예약하는 것입니다. 하지만 이렇게 자동으로 예약하는 행위 이외에도 수동으로 예약할 수 있는 메소드도 존재합니다. 그 방법이 setNeedsLayout()와 layoutIfNeeded() 입ㅂ니다.
간단하게 setNeedsLayout과 layoutIfNeeded를 공식 문서에서 보면
이렇게 설명이 되어있습니다. 즉 setNeedsLayout은 다음 update Cycle에서 layoutIfNeeded는 즉시 갱신을 요청합니다.
그럼 쫌더 상세하게 파보면
- setNeedsLayout()
layoutSubviews를 예약하는 행위 중 가장 비용이 적게 드는 방법이 setNeedsLayout을 호출하는 것입니다. 이 메소드를 호출한 View는 재계산되어야 하는 View라고 수동으로 체크가 되며 update cycle에서 layoutSubviews가 호출되게 됩니다.
이 메소드는 비동기적으로 작동하기 때문에 호출되고 바로 반환됩니다. 그리고 View의 보여지는 모습은 update cycle에 들어갔을 때 바뀌게 됩니다.
- layoutIfNeeded()
이 메소드는 setNeedsLayout과 같이 수동으로 layoutSubviews를 예약하는 행위이지만 해당 예약을 바로 실행시키는 동기적으로 작동하는 메소드입니다. update cycle이 올 때까지 기다려 layoutSubviews를 호출시키는 것이 아니라 그 즉시 layoutSubviews를 발동시키는 메소드입니다.
만일 main run loop에서 하나의 View가 setNeedsLayout을 호출하고 그 다음 layoutIfNeeded를 호출한다면 layoutIfNeeded는 그 즉시 View의 값이 재계산되고 화면에 반영하기 때문에 setNeedsLayout이 예약한 layoutSubviews 메소드는 update cycle에서 반영해야할 변경된 값이 존재하지 않기 때문에 호출되지 않습니다.
이러한 동작 원리로 layoutIfNeeded는 그 즉시 값이 변경되어야 하는 애니매이션에서 많이 사용됩니다. 만일 setNeedsLayout을 사용한다면 애니매이션 블록에서 그 즉시 View의 값이 변경되는 것이 아니라 추후 update cycle에서 값이 반영되므로 값의 변경은 이루어지지만 애니매이션 효과는 볼 수 없는 것입니다.
setNeedsLayout과 layoutIfNeeded의 차이점은 동기적으로 동작하느냐 비동기적으로 동작하느냐의 차이입니다.
마지막으로 아래 그림은 view와 viewController의 layout관련 메소드의 호출 순서입니다.
레이아웃을 조정하기 위해선 아래 순서는 숙지해야할 것 같습니다!!
그리고 setNeedsLayout과 layoutIfNeeded 이외에도 아래 블로그에서 몇몇 view 관련 메소드를 확인 하실 수 있습니다.
https://zeddios.tistory.com/359?category=682195
출처 : https://baked-corn.tistory.com/105
출처 : https://ontheswift.tistory.com/20
'2023년 이전 > iOS' 카테고리의 다른 글
SwiftUI - tutorial (Building Lists and Navigation and Generating Previews Dynamically) (0) | 2020.07.19 |
---|---|
SwiftUI - tutorial(Creating and Combinding Views) (0) | 2020.07.14 |
UIApplication, Main Run Loop, AppDelegate (0) | 2020.06.28 |
iOS에서 underscore(_) 는 무엇이고, unrecognized selector sent to instance 은 왜 발생할까? (0) | 2020.06.14 |
iOS - .xib에서 TableView 와 User Interaction Enabled (0) | 2020.06.14 |