iOS 화면 회전 처리

최근 프로젝트 도중 화면 회전에 관련된 코드를 사용해 보았다. 비교적 간단한 내용이지만 나에게 다시 상기시키고자 정리해본다.

프로젝트 앱은 기본적으로 아이폰의 세로모드만 지원했지만 영상을 관리하는 뷰 컨트롤러를 새로 만들면서 비디오가 재생되는 곳은 가로모드를 지원하게 하고 싶었다.

전체적인 방향 설정

먼저 앱의 전체적인 방향 설정부터 알아보자.

Supported interface orientations

앱은 기본적으로 Info.plistSupported interface orientations를 참조한다. 다음 그림은 프로젝트 생성 후 Info.plistSupported interface orientations 키의 디폴트 값을 보여준다.

정리해보면,

  • iPhone
    • Portrait: bottom home button
    • Landscape: left home button
    • Landscape: right home button
  • iPad

    • Portrait: bottom home button
    • Landscape: left home button
    • Landscape: right home button
    • Portrait: top home button)

    Info.plist에서 이 값들을 지우거나 더함으로써 앱의 기본적인 방향을 정해줄 수 있다.

supportedInterfaceOrientations(for:)

두번째는 AppDelegateapplication(_:supportedInterfaceOrientationsFor:) 함수를 사용하는 것이다.

1
2
3
4
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?)
-> UIInterfaceOrientationMask {
return [.portrait, .portraitUpsideDown, .landscapeLeft]
}

앱이 지원하는 방향을 설정할 수 있으며 옵셔널한 메소드이기 때문에 구현을 하지 않을시 Info.plist의 지원 방향을 참조한다. 이 메소드는 OptionSet을 준수하는 UIInterfaceOrientationMask 구조체를 리턴하며 iOS 기기의 모든 방향이 정리되어 있다.

공식 문서에는 이 메소드에 대해 이렇게 나와 있다.

When determining whether to rotate a particular view controller, the orientations returned by this method are intersected with the orientations supported by the root view controller or topmost presented view controller. The app and view controller must agree before the rotation is allowed.

즉, 어떤 뷰 컨트롤러를 돌리는 것을 결정할 때 이 메소드에서 지원하는 방향이 그 뷰가 돌리려는 방향을 지원하지 않으면 돌릴 수 없다는 말이다. 이 메소드가 앱의 가장 상위에서 방향을 제어하고 있다고 볼 수 있다. 이것은 특정 뷰 컨트롤러의 방향을 설정할 때 중요한데 예를 들어 전체적인 앱의 방향은 세로로 고정되어 있는데 특정 뷰 컨트롤러는 가로 모드도 지원을 하고 싶다면 이 AppDelegate 메소드에서 가로 모드를 명시해줘야 한다는 뜻이다.

View Controller의 방향 설정

다음은 개별 ViewController의 방향 설정이다.

shouldAutorotate

먼저 shouldAutorotate 프로퍼티를 보자.

1
2
3
4
5
6
var shouldAutorotate: Bool { get }

// 뷰 컨트롤러 내에서 오버라이드하여 사용합니다.
override var shouldAutorotate: Bool {
return true // or false
}

뷰 컨트롤러가 돌아가는 것을 결정하는 Bool 타입의 뷰 컨트롤러 인스턴스 프로퍼티이다. 뷰 컨트롤러 내에서 self로 접근하면 읽는 것만 가능하지만 상위 클래스인 UIViewController의 프로퍼티를 오버라이드하여 수정할 수 있다.

supportedInterfaceOrientations

그 다음으로 supportedInterfaceOrientations 프로퍼티를 보자.

1
2
3
4
5
6
7
var supportedInterfaceOrientations: UIInterfaceOrientationMask { get }

// 마찬가지로 오버라이드하여 사용합니다.
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return [.portrait, .portraitUpsideDown]
// 그밖에도 .landscapeLeft, .landscapeRight, .landscape, .all, .allButUpsideDown이 있습니다.
}

뷰 컨트롤러가 지원하는 방향을 가리키는 인스턴스 프로퍼티로 역시 오버라이드하여 변경 가능하다. 이 메소드는 shouldAutorotate 메소드가 true 값을 반환할 때만 불리게 되며 이 메소드에서 지원하는 방향이 위에서 말한 앱 전체적 지원 방향에 들어가 있다면 그 방향으로 회전하는 것이 가능하다.

이 두가지 메소드를 통해 뷰 컨트롤러의 회전을 제어할 수 있다. 여기서 중요한 것은 만약 뷰 컨트롤러의 상위 뷰 컨트롤러가 있다면 그 뷰 컨트롤러의 두 메소드를 따른다는 것이다. 예를 들어 UINavigationController가 Portrait 모드만 지원한다면 그 안에 있는 ViewController에서 Landscape를 지원한다고 명시해도 지원할 수 없다.

추가적으로 화면을 뷰가 전환되거나 할 때 강제로 뷰의 방향을 바꾸고 싶다면 currentDevice의 key-value로 접근하여 바꿀 수 있다.

1
2
let orientationValue = UIInterfaceOrientationMask.landscapeLeft.rawValue
UIDevice.current.setValue(orientationValue, forKey: "orientation")

Reference