뷰 컨트롤러는 뷰의 계층도를 관리해주는 오브젝트로
다음과 같은 주로 다음과 같은 책임을 지는 객체입니다.
해당 객체를 바로 선언하여 사용하는 일은 드물고 주로 UIViewController
를 상속하고 필요한 메서드를 재정의하거나 메서드와 프로퍼티 등을 추가하여 사용합니다.
또한 뷰 컨트롤러는 자신만의 뷰 세트를 갖게 됩니다.
여기서 중요한 점은 뷰 컨트롤러는 뷰와 서브뷰의 유일한 소유자여야 한다는 것입니다.
따라서 생성과 소멸주기도 해당 뷰 컨트롤러에게 관리할 책임이 있습니다.
스토리보드 등으로 뷰를 요청한다면 UIKit
이 자동으로 해당 뷰 컨트롤러를 위한 뷰 객체를 생성하지만 코드로 직접 뷰를 생성하는 경우는 사용자가 주의를 기울여야 합니다.
만일 여러 뷰 컨트롤러가 동일한 뷰를 공유하게 된다면 뷰와 관련된 작업의 책임을 어디서 가져가야 할지 불분명해지고 상태 관리가 복잡해질 수 있습니다.
또 뷰 컨트롤러가 해제되었으나 공유된 뷰가 남아있으면 의도치 않은 동작이 발생하거나 메모리 누수가 발생할 확률도 높아지게 됩니다.
뷰 컨트롤러에는 앱과 같이 생명주기가 존재합니다.
그리고 각 생명주기에 맞게 호출되는 메서드들이 존재합니다.
순서를 다시 정리해보자면 다음과 같습니다.
init
load view
view did load
view will appear
view is appearing
view did appear
view will disappear
view did disappear
deinit
처음 초기화 후 메모리에 로드됩니다.
이후 사용자에게 나타나는 과정이 3단계로 나누어지고 다른 뷰 컨트롤러로 이동하는 등의 이유로 사용자가 볼 수 없게 될 때 사라지는 과정이 2단계로 이루어져있습니다.
이후 완전히 필요없어진다면 객체는 메모리에서 내려가게 됩니다.
다음 이미지는 사용자가 뷰컨트롤러를 볼 수 있는 경우와 상태 전환을 나타내고 있습니다.
여기서 will
메서드는 주로 did
메서드와 쌍을 이루게 됩니다.
그렇다고 반드시 저 둘만이 짝을 이루는 것은 아니고 반대편의 will
메서드와도 쌍을 이루기도 합니다.
did
와 정확히 짝지어지지 않는 경우는 화면 전환 중 인터럽트가 발생하거나 생명주기 단계를 건너뛰는 경우 did
메서드가 호출되지 않을 수 있습니다.
또는 애니메이션의 경우 뷰가 완전히 나타나기 전까지 진행되다가 나타난 후 종료하는 것이 자연스러우나 네트워크 처리같은 경우엔 뷰 컨트롤러가 보여지는 동안 사용하다가 다른 화면으로 넘어가기 시작할 때 즉, 반대편 will
메서드가 실행될 때 종료해야 자연스럽습니다.
중요한 건 will
메서드에서 시작한 메서드는 반드시 쌍을 이루는 did
메서드에서든 반대편 will
메서드에서든 종료되어야 한다는 것입니다. 그리고 작업의 특성에 따라 적절한 종료 시점은 다른 생명 주기에 위치할 수 있다는 점입니다.
Appear
의 단계는 Disappear
와 달리 중간에 한 단계가 더 존재합니다.
왜 그럴까요?? 왜 Disappear
는 차별받은 걸까요?
먼저 appear
과 관련하여 기존의 will
과 did
만으로 이루어진 상태에서는 정밀한 애니메이션이나 UI처리가 어려웠습니다.
will
이 호출되는 시점에선 뷰의 위치, 크기, 트레이트 등이 지정되지 않은 경우가 존재하고,
did
가 호출되는 시점은 이미 사용자에게 모든 화면이 표시되었으므로 UI 처리가 늦을 수 있습니다.
In contrast to viewWillAppear(_:), the system calls this method after it adds the view controller’s view to the view hierarchy, and the superview lays out the view controller’s view. By the time the system calls this method, both the view controller and its view have received updated trait collections and the view has accurate geometry.
이러한 문제를 해결하기 위한 is appearing
라이프 구간은 생각보다 최근인 WWDC2023
에서 추가되었습니다.
공식문서에 따르면 viewWillAppear
와 달리 viewIsAppearing
은 뷰 컨트롤러의 뷰가 뷰 계층에 추가되고 상위뷰가 뷰 컨트롤러의 뷰를 모두 레이아웃 한 이후 호출됩니다.
즉 보다 적절한 타이밍에 UI 업데이트를 제공할 수 있도록 돕기 위해 추가된 것으로 뷰가 사라지는 과정에는 이러한 세부적인 조정이 필요 없기에 isDisappearing 메서드가 없는 듯 합니다.
사진에서 보듯 willAppear
와 isAppearing
은 콜백되는 순서의 차이가 존재하나 모두 동일한 CATransaction
내에서 발생합니다.
따라서 사용자는 동시에 두 메서드의 처리 결과를 보게 됩니다.
대신 앞서 말했듯 isAppearing
은 모든 UI의 트레이트나 지오메트리가 최신 상태로 업데이트 된 상태에서 호출되므로 애플은 뷰를 업데이트 할 때 willAppear
대신 isAppearing
을 사용할 것을 권장하고 있습니다.
그리고 다음과 같은 경우에만 willAppear
를 사용하여 처리할 것을 권합니다.
transitionCoordinator
에 접근하여 함께 실행 될 alongside
애니메이션을 추가해야 하는 경우.alongside animation
이란 뷰 컨트롤러 전환 애니메이션과 동시에 프레임워크에서 실행하도록 지시하는 애니메이션을 의미)willAppear
에서 데이터베이스 알림을 등록하고, didDisappear
에서 이를 등록 해제하는 경우와 같이 균형 잡힌 콜백이 필요한 작업.
추가로 위 표를 참고하면 좀 더 메서드를 호출할 시점을 정하는 데 도움이 될 것입니다.
마지막으로 살펴볼 것은 앞서 봤던 view
로 시작되는 메서드 목록 중 라이프 사이클에 포함되지 않는 메서드 2개입니다.
먼저 알아볼 것은 layoutSubviews
로 뷰의 하위뷰(subview
)를 재배치하거나 조정할 때 사용되는 메서드입니다.
이 메서드는 하위 뷰가 추가, 삭제 되거나 위치가 바뀌는 등의 상황에서 자동으로 호출됩니다.
애플은 서브뷰의 레이아웃과 관련하여 원하는 동작이 자동으로 지원되지 않을 때만 재정의할 것을 권장합니다.
추가로 이 메서드를 직접 호출하는 대신 다음 UI 업데이트 이전에 setNeedsLayout
를 호출 할 것을 권장하고 있습니다.
만약 즉시 레이아웃을 업데이트 하려면 layoutIfNeeded
를 호출하라고 합니다.
이렇게 보통은 자동으로 처리되는 layoutSubviews
전후로 호출되는 viewWillLayoutSubviews
와 viewDidLayoutSubviews
가 마지막 목차의 주인공입니다.
이 두 메서드는 layoutSubviews
가 실행될 때마다.
즉 전환이 일어나는 상황이라면 뷰가 표시되는 상황동안 몇 번이든 호출될 수 있습니다.
이 부분이 단 한 번만 실행되는 viewIsAppearing
과 가장 큰 차이점입니다.
다른 차이로는 layoutSubviews
과 관련 메서드들은 레이아웃이 조정될 때 호출된다면 viewIsAppearing
은 레이아웃이 조정되지 않을 때도 호출됩니다.
따라서 반복적인 호출이 필요한 UI업데이트 메서드는 layoutSubviews
관련 메서드에, 단 한 번만 필요한 초기화나 뷰 업데이트 등은 viewIsAppearing
에 알맞게 넣어야 합니다.
호출 조건과 목적이 다르다는 점을 염두에 두고 상황에 잘 맞게 선택해야 합니다.
Apple 공식 문서: viewIsAppearing
Apple 공식 문서: Displaying and Managing Views
Apple 공식 문서: UIViewController 전체
우주최초 계엄령 중에 TIL 쓴 사람