iOS/UIKit

디바이스 회전 대응

Hamp 2025. 4. 26. 22:28
반응형

👋 들어가기 전

최근 들어 iPad 앱을 도전해보고 싶은 마음이 점점 커지고 있다.

그래서 앱에서 디바이스를 회전했을 때 대응하는 과정을 한번 학습해보고 싶었다.

🏁 학습할 내용

먼저 가장 필요한 내용이 화면 회전 시 호출될 생명주기와

대응코드가 들어갈 위치를 알아보자.

 

이후 엣지 케이스 대응을 위한 추가학습 미리보기까지 정리해보자. 

  • viewWillTransition
  • layoutSubview

🔄  viewWillTransition

⭐️ 정의

공식문서를 살펴보면 viewWillTransition은 뷰들을 포함하는 컨테이너의 사이즈가
변경되면 호출된다고 써 있다.

 

가장 대표적인 컨테이너 사이즈 변화는 디바이스 회전이 있다.

아이패드 일 때는 스플릿 뷰도 포함된다.

🔥 구성요소

 override func viewWillTransition(
    to size: CGSize,
    with coordinator: any UIViewControllerTransitionCoordinator
  ) {
    
  }

 

1. size

  • 컨테이너의 변화된 새로운 사이즈

2. coordinator

  • 전환에 대한 진행 정보를 제공하고, 변화를 관리하는 객체

⌨️ 실습

✅ 증명

먼저 언제 호출되는 지 살펴보자.

위에서 설명한대로 회전과 스플릿을 진행

 

예상 했던대로 회전과 스플릿 뷰에서 모두 호출된는 것을 검증했다.

🍎 animate vs animateAlongsideTransition

coordinator에서 제공하는 api를 보면 animation과 관련된 2개의 메서드가 있다.

역활과 차이점을 살펴보자.

구분 animate animateAlongsideTransition
스타일 최신 구버전
여갛ㄹ 전환 애니메이션에서 추가로 실행될 애니메이션 적용

🖼️ layoutSubviews()

⭐️ 정의

자신의 자식 뷰들의 배치를 잡을 때 호출된다.

여기서 배치를 잡을 때는 재배치도 포함된다.

 

재배치는 언제 이뤄질까?

 

프레임 크기가 변경되거나 제약조건이 변경되거나 다양한 경우가 존재하지만

결국에는 시스템이 updateCycle 시 갱신이 필요하다는 변화가 느껴져야 호출된다.


⌨️ 실습

한번 지금까지 배운 개념을 잠깐 적용해보자.

 

가로모드와 세로모드일 때 AView의 높이를 변경해보고

layoutSubviews가 호출되는 지 살펴보자.

AView.swift

class AView: UIView {

  private let label: UILabel = {
    let label = UILabel()
    label.text = "123123124141"
    return label
  }()

  override func layoutSubviews() {
    super.layoutSubviews()
    print(#function)
  }

  init() {
    super.init(frame: .zero)
    self.addSubview(label)

    label.translatesAutoresizingMaskIntoConstraints = false
    label.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
    label.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
    label.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
    label.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true

    label.textColor = .blue
  }
  
  required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

}

ViewController.swift

final class ViewController: UIViewController {
  private let aView = AView()
  private var portraitConstraints: NSLayoutConstraint?
  private var landscapeConstraints: NSLayoutConstraint?

  override func viewWillTransition(
    to size: CGSize,
    with coordinator: any UIViewControllerTransitionCoordinator
  ) {
    let isPortrait = size.width < size.height
    coordinator.animate(alongsideTransition: nil) { [weak self] _ in
      guard let self else { return }

      if isPortrait {
        landscapeConstraints?.isActive = false
        portraitConstraints?.isActive = true
      } else {
        portraitConstraints?.isActive = false
        landscapeConstraints?.isActive = true
      }
    }
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = .white
    view.addSubview(aView)
    aView.translatesAutoresizingMaskIntoConstraints = false

    aView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    aView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

    aView.backgroundColor = .black

    portraitConstraints = aView.heightAnchor.constraint(equalToConstant: 100)
    landscapeConstraints = aView.heightAnchor.constraint(equalToConstant: 200)

    portraitConstraints?.isActive = true

  }
}

결과

예측한 동작이 작동해서 너무 좋다...


❓그러면 여기서 질문

UICollectionView와 UITableView와 같은 cell을 통한 데이터를 표현할 때

cell들의 layoutSubview()를 어떻게 호출하고

 

데이터는 그대로이고 레이아웃의 변화가 일어날 때 어떤 일들이 일어날까??

 

그 내용은 UICollectionView와 UITableView 파트로 나눠서 다음 포스팅에서 진행하겠다..


😀 소감 및 마무리

언젠간 꼭 이 지식들을 이용해 가로모드도 자연스럽게 지원되는 앱을 만들고 싶다.


출처

https://developer.apple.com/documentation/uikit/uicontentcontainer/viewwilltransition(to:with:)

 

viewWillTransition(to:with:) | Apple Developer Documentation

Notifies the container that the size of its view is about to change.

developer.apple.com

https://developer.apple.com/documentation/uikit/uiview/layoutsubviews()

 

layoutSubviews() | Apple Developer Documentation

Lays out subviews.

developer.apple.com

https://hamp.tistory.com/23

 

생명주기 (3) [ View 생명주기 ]

오늘은 iOS 생명주기의 마지막 단계인 View의 생명주기에 대해 알아보자. 전체적인 생명주기는 다음 그림과 같다. init1) init((coder:))스토리보드나 xib파일은 활용하여 화면을 만들 때 컴파일러가

hamp.tistory.com

https://hamp.tistory.com/24

 

생명주기 (4) [ 업데이트 Cycle ]

애니메이션과 트랜지션과 같은 조금 더 개선된 UX를 주기 위해 다양한 코드를 적었는데 원하는 대로 동작하지 않는 경우가 자주 발생하고 나는 그게 View Rendenring을 재대로 이해하지 못하고 있는

hamp.tistory.com

 

반응형