이게 얼마만의 iOS 포스팅인가.
부끄럽지만 정말 오랜만에 iOS 관련 공부를 다시 시작했고 면접을 겪으며 공부할 필요성을 느꼈던 내용들 위주로 하나씩 공부해보려 한다.
오늘은 UIView 의 위치, 모양 변화를 주면서 차이를 잘 모르고 아무렇게나 사용해왔던, setNeedsLayout() 과 layoutIfNeeded() 메소드에 대해 공부해보았다.
먼저 두 메소드에 대해 알아보기 이전에 Main Run Loop 와 Update Cycle 의 개념이 필요하다.
어플리케이션이 실행되면 UIApplication 이 메인 스레드에서 main run loop 를 실행시키게 되고 main run loop 에선 터치 이벤트, 위치 변화, 디바이스 회전 등의 이벤트들을 처리 하게 된다.
이때 이러한 처리 과정은 각 이벤트마다 알맞는 핸들러를 찾아 각 핸들러에게 처리 권한을 위임하는 방식으로 진행되는데 (UIButton 의 터치 이벤트 처리를 @IBAction 이 담당)
이벤트들 처리가 모두 끝난 후 권한이 다시 main run loop로 돌아오는 시점을 update cycle 이라 한다!
즉, update cycle은 어플리케이션이 유저로부터 받은 이벤트들에 대한 핸들링을 진행한 후 다시 main run loop로 권한 및 컨트롤을 반환하는 시점을 말한다!
하지만 main run loop 에서 발생하는 이벤트들에 대한 핸들링은 즉각적으로 그 변화가 반영되는 것이 아니다.
시스템은 layout 이나 position이 변화하는 view들을 체크하고 모든 핸들러가 종료된 후 update cycle 에서 체크해 두었던, 변화가 필요한 view 들의 값을 바꿔주게 된다.
다시말해 view의 layout, position 값을 변경하는 코드의 실행 시점과 실제로 그 변화가 반영되는 시점 사이에 시간차가 발생하게 된다!
main run loop와 update cycle에 대한 개념이 잡혔다면 uiview 에 내장된 메소드들 중에서 layoutSubviews()와, 오늘 알아볼 두 메소드 setNeedsLayout(), layoutIfNeeded() 를 함께 알아보자!
UIView의 메소드 중 하나인 layoutSubviews()는 호출시 UIView size나 poisition 값을 변화 시켜주는 역할을 하는 메소드이다.
이 메소드를 호출하면 해당 view의 모든 하위 view들을 따라 layoutSubviews()가 재귀적으로 호출되게 되고 자연스레 비용이 큰 메소드임을 알 수 있다. 때문에 직접 이 메소드를 호출하는 것은 지양해야 한다.
이 메소드는 시스템에 의해서 view의 값이 재계산 되어야 하는 시점, update cycle 에 자동으로 호출되게 되고 이를 통해 view의 size, position, layout 등의 값들의 변화가 반영되게 되는 것이다.
이러한 layoutSubviews()를 직접 부르지 않고 update cycle 에서의 호출을 자동으로 예약하는 상황들이 존재하는데
와 같은 상황들이 그러하다! 위와 같은 상황이 발생하면 자동적으로 update cycle 에서 해당 view의 layoutSubviews()가 호출되는 것이다.
하지만 위와 같은 상황 이외에, 수동으로 layoutSubviews() 호출을 예약하는 메소드가 존재하는데 그것이 바로 setNeedsLayout(), layoutIfNeeded() 인 것이다!!
setNeedsLayout()을 호출하면 해당 view는 재계산이 필요한 view라고 수동으로 체크된다.
비동기적으로 동작하기 때문에 호출 뒤 바로 반환되고 main run loop 가 끝난 뒤 update cycle에 들어갔을 때 값을 재계산해 view의 size, position 등의 값이 변화하게 된다.
적은 비용으로 update cycle 에서 layoutSubviews() 를 예약할 수 있는 메소드이다.
layoutIfNeeded() 역시 setNeedsLayout() 와 같이 수동으로 layoutSubviews() 를 예약하는 메소드이다.
하지만 차이점은 setNeedsLayout() 과는 다르게 동기적으로 동작한다는 점이다.
호출시 바로 layoutSubviews() 예약을 실행시켜 변경 사항이 반영되기 때문에 애니메이션과 같은 상황에 사용할 수 있다.
예를 들어 main run loop 에서 어떠한 view가 setNeedsLayout()을 호출 뒤 layoutIfNeeded()를 바로 호출한다면 변경 사항은 그 즉시 반영되기 때문에 update cycle 에서 setNeedsLayout() 호출 시 예약되었던 layoutSubviews()는 호출되지 않게 된다!
setNeedsLayout() 과 layoutIfNeeded() 는 모두 update cycle에서의 layoutSubviews() 호출을 예약하는 메소드 이고 둘은 비동기적, 동기적 작동 차이가 존재하는 것이었다!