Adaptivity and Size Changes

Panther·2021년 7월 24일
0

The Adaptive Model

적응형 인터페이스는 사용 가능한 공간의 최대한 사용할 수 있게 해주는 인터페이스입니다. 적응형이 된다는 것의 의미는 컨텐트를 조정할 수 있음을 의미하는 것이며, 그렇게 함으로써 모든 iOS 기기에 최적화될 수 있도록 해줍니다. iOS에서 적응형 모델은 변경사항에 대한 재정렬과 크기 재조정을 위한 간단하면서도 동적인 방법들을 지원합니다. 이 모델의 이점을 활용하면 앱은 적은 코드로도 다른 스크린 크기(Figure 12-1에 나와있는 것처럼)에 적응할 수 있습니다.

Figure 12-1 Adapting to different devices and orientations

적응형 인터페이스를 설계하는 중요한 도구는 오토 레이아웃입니다. 오토 레이아웃을 사용하면 뷰 컨트롤러의 뷰에 대한 레이아웃을 제어하는 규칙(제약으로 알려져 있는)을 정의합니다. 인터페이스 빌더 혹은 코드 작성을 통해 이 규칙들을 생성할 수 있습니다. 부모 뷰의 크기가 변경될 때, iOS는 구체화된 제약에 따라 남은 뷰들의 크기와 위치를 자동으로 조정합니다.

특성 역시 적응형 모델의 중요한 요소입니다. 특성은 뷰 컨트롤러와 뷰가 작동해야 하는 환경을 설명합니다. 특성은 인터페이스에 대한 고수준 의사결정을 도와줍니다.

The Role of Traits

제약이 레이아웃을 다루는 데 충분하지 않을 때, 뷰 컨트롤러는 변경사항을 만들 수 있는 몇 가지 기회를 갖습니다. 뷰컽느롤러, 뷰, 몇 가지 객체는 해당 객체와 연관된 현재 환경을 구체화해주는 특성의 집합을 관리합니다. Table 12-1은 특성과 이 특성을 UI에 적용하기 위해서 어떻게 사용해야 하는지에 대한 설명입니다.

Table 12-1 Traits

TraitExamplesDescription
horizontalSizeClassUIUserInterfaceSizeClassCompact이 특성은 인터페이스의 일반적인 넓이를 전달합니다. 뷰가 세로로 쌓이는지, 양옆으로 놓이는지, 모두 숨겨지는제, 다른 수단에 의해 보여지는지 여부와 같은 대략적인 수준의 레이아웃 의사결정을 내리기 위해 사용할 수 있습니다.
verticalSizeClassUIUserInterfaceSizeClassRegular이 특성은 인터페이스의 일반적인 높이를 전달합니다. 만약디자인이 스크롤링 없이도 화면에 모든 컨텐트를 채워주길 요구한다면, 레이아웃 의사결정을 내리기 위해 이 특성을 사용해야 합니다.
displayScale2.0이 특성은 컨텐트가 레티나 디스플레이로 보여져야 하는지 혹은 표준 레졸루션 디스플레이로 보여져야 하는지 여부를 전달합니다. 필요한 경우 픽셀 수준의 레이아웃이 필요한지 의사결정해야 하는 것과 이미지를 표시하기에 어떤 버전을 선택해야 하는지를 위해 이 특성을 사용하면 됩니다.
userInterfaceIdiomUIUserInterfaceIdiomPhone이 특성은 이전 버전에 대한 호환성을 위해 제공되고, 앱이 실행되는 기기의 타입을 전달합니다. 가급적 이 특성은 사용하지 않아야 합니다. 대신 레이아웃 의사결정과 관련해 수평과 수직 크기 클래스를 사용하는 것이 좋습니다.

UI를 어떻게 제시할 것인지를 결정하기 위해 특성을 사용하시기 바랍니다. 인터페이스 빌더에서 인터페이스를 설계할 때, 보여주고자 하는 뷰와 이미지를 변경시키기 위해 특성을 사용하시기 바랍니다. 혹은 제약을 달리 적용하기 위해 특성을 사용하시기 바랍니다. UIImageAsset과 같은 많은 UIKit 클래스가 구체화한 특성을 사용해 제공하는 정보를 조정합니다.

다음은 다른 유형의 특성을 언제 사용하는지를 이해할 수 있도록 돕는 팁들입니다.

  • 인터페이스에 대한 대략적인 변경을 만들기 위해 사이즈 클래스를 사용해야 합니다. 사이즈 클래스 변경은 뷰의 추가 혹은 삭제, 자식 뷰 컨트롤러의 추가 혹은 삭제, 레이아웃 제약의 변경에 있어 적합한 시기입니다. 다른 적용 없이 인터페이스고 자동으로 적응하도록 할 수도 있습니다.
  • 사이즈 클래스가 뷰의 특정 넓이 혹은 높이에 상응한다고 가정하지 않아야 합니다. 뷰 컨트롤러의 사이즈 클래스는 여러 가지 이유로 변경될 수 있습니다. 예를 들어 iOS의 컨테이너 뷰 컨트롤러는 컨텐츠를 다르게 표시하도록 강제하도록 자식 뷰 컨트롤러를 수평 regular로 만들 수 있습니다.
  • 각각의 사이즈 클래스에 대해 다른 레이아웃 제약을 구체화하기 위해 인터페이스 빌더를 사용하시기 바랍니다. 제약을 구체화하기 위해 인터페이스 빌더를 사용하는 것은 제약을 직접 추가하고 제거하는 것보다 더 간단합니다. 뷰 컨트롤러는 스토리보드로부터 적합한 제약을 적용해 사이즈 클래스 변경을 자동으로 처리합니다. 다른 사이즈 클래스마다 적합한 레이아웃 제약을 설정하는 것에 대한 더 많은 정보는 Configuring Your Storyboard to Handle Different Size Classes를 살펴보시기 바랍니다.
  • 인터페이스의 레이아웃 혹은 컨텐트에 대한 의사결정을 내리기 위해 idom 정보를 사용하는 것은 피하시기 바랍니다. 아이패드 혹은 아이폰에서 작동하는 앱은 같은 정보를 표시해야 하며, 레이아웃 의사결정을 내리기 위한 사이즈 클래스를 사용해야만 합니다.

Configuring Your Storyboard to Handle Different Size Classes는 아래 부분에 등장합니다.

When Do Trait and Size Changes Happen?

특성은 자주 변경되지 않지만 변경될 때도 있습니다. UIKit은 환경의 변화에 기초해 뷰 컨트롤러의 특성을 업데이트 합니다. 사이즈 클래스 특성은 디스플레이 스케일 특성보다 변경될 가능성이 더 큽니다. idiom 특성은 거의 변경되지 않아야 합니다. 사이즈 클래스 변경은 아래와 같은 이유로 발생합니다.

  • 뷰 컨트롤러의 윈도우 수직 혹은 수평 사이즈 클래스가 주로 기기 회전에 의해 변경됩니다.
  • 컨테이너 뷰 컨트롤러의 수평 혹은 수직 사이즈 클래스가 변경됩니다.
  • 현재 뷰 컨트롤러의 수평 혹은 수직 사이즈 클래스가 컨테이너에 의해 명시적으로 변경됩니다.

뷰 컨트롤러 계층구조에 있는 사이즈 클래스 변경은 모든 자식 뷰 컨트롤러로 전파됩니다. 윈도우 객체는 루트 뷰 컨트롤러에 대한 베이스라인 사이즈 클래스를 제공하면서 윈도우 객체는 해당 계층구조의 루트로써 역할을 합니다. 기기가 portrait와 landscape 사이에서 변경이 있을 때, 윈도으는 스스로 크기 클래스 정보를 업데이트하고, 뷰 컨트롤러 계층구조의 아래 방향으로 전파합니다. 컨테이너 뷰 컨트롤러는 수정되지 않은 자식 뷰 컨트롤러에 변경사항을 전달하거나 각 자식의 특성을 오버라이드할 수 있습니다.

iOS 이상의 버전에서 윈도우 오리진은 항상 좌측 상단 모서리이고, 기기가 landscape와 portrait 사이에서 전환될 때 윈도우의 bounds는 변경됩니다. 윈도으 크기 변경은 상응하는 모든 특성을 따라 뷰 컨트롤러 계층구조 아래 방향으로 전파됩니다. 계층구조에서 각 뷰컨트롤러의 경우 UIKit은 이와 같은 변경을 알리기 위해 아래 메소드를 호출합니다.

  1. willTransitionToTraitCollection:withTransitionCoordinator:는 각각의 연관있는 뷰 컨트롤러에게 특성이 변경될 준비가 되었다고 알려줍니다.
  2. viewWillTransitionToSize:withTransitionCoordinator:는 각각의 연관있는 뷰 컨트롤러에게 크기 변경이 준비되었음을 알려줍니다.
  3. traitCollectionDidChange:는 각각의 연관있는 뷰 컨트롤러에게 특성이 변경되었음을 알려줍니다.

뷰 컨트롤러 계층구조를 탐색할 때, UIKit은 알려줘야 하는 변경사항이 있을 때에만 뷰 컨트롤러에게 변경사항을 알려줍니다. 컨테이너 뷰 컨트롤러가 자식의 사이즈 클래스를 오버라이드하고 있다면, 자식들은 컨테이너의 사이즈 클래스가 변경될 때 알림을 받지 않습니다. 비슷하게 뷰 컨트롤러의 뷰가 고정된 넓이와 높이를 갖는다면, 크기 변경의 알림을 받지 않습니다.

Figure 12-2는 아이폰6에서 기기 전환이 발생할 때 어떻게 뷰 컨트롤러의 특성과 뷰 크기가 업데이트되는지를 보여줍니다. portrait에서 landscape로의 전환은 스크린의 수직 사이즈 클래스를 regular에서 compact로 변경합니다. 사이즈 클래스 변경과 상응하는 뷰 크기의 변경은 뷰 컨트롤러 계층구조를 따라 아래로 전파됩니다. 뷰가 새로운 크기로 애니메이션 처리된 이후 UIKit는 뷰 컨트롤러의 traitCollectionDidChange: 메소드가 호출되기 전에 사이즈 클래스와 뷰 사이즈 변경사항을 적용합니다.

Figure 12-2 Updating a view controller’s traits and view size

Default Size Classes for Different Devices

각각의 iOS 기기는 인터페이스 설계 시 가이드로 사용할 수 있는 사이즈 클래스의 기본값 설정을 갖고 있습니다. Table 12-2는 portrait와 landscape 방향에서 기기에 대한 사이즈 클래스릐 목록입니다. 테이블에 없는 기기는 같은 스크린 차원을 갖는 기기와 동일한 사이즈 클래스를 갖습니다.

Table 12-2 Size classes for devices with different screen sizes.

DevicePortraitLandscape
iPad (all)
iPad Mini
Vertical size class: Regular
Horizontal size class: Regular
Vertical size class: Regular
Horizontal size class: Regular
iPhone 6 PlusVertical size class: Regular
Horizontal size class: Compact
Vertical size class: Compact
Horizontal size class: Regular
iPhone 6Vertical size class: Regular
Horizontal size class: Compact
Vertical size class: Compact
Horizontal size class: Compact
iPhone 5s
iPhone 5c
iPhone 5
Vertical size class: Regular
Horizontal size class: Compact
Vertical size class: Compact
Horizontal size class: Compact
iPhone 4sVertical size class: Regular
Horizontal size class: Compact
Vertical size class: Compact
Horizontal size class: Compact

IMPORTANT
기기에서 앱이 특정 사이즈 클래스로 표시될 것이라고 가정하지 않아야 합니다. 객체를 어떻게 설정할 것인지에 대해 의사결정할 때, 객체의 특성 집합에서 찾을 수 있는 사이즈 클래스를 항상 확인해야 합니다.

Building an Adaptive Interface

적응형 인터페이스는 특성과 크기 변화 모두에 반응해야 합니다. 뷰 컨트롤러 수준에서 보여주고자 하는 컨텐트와 해당 컨텐트의 레이아웃에 대한 대략적인 수준을 결정하기 위해 특성을 사용해야 합니다. 예를 들어 사이즈 클래스가 변화할 때, 뷰 특성의 변경을 선택하게 될 것입니다. 특성은 뷰를 나타내거나 숨기는 것 혹은 완전히 다른 집합의 뷰를 표시하는 것에 해당합니다. 이와 같은 의사결정이 만들어진 이후 컨텐트를 미세하게 조정하기 위해 사이즈 변경을 사용하게 될 것입니다.

Adapting to Trait Changes

특성은 다른 환경에 대해 앱이 다른 방식으로 설정할 수 있는 방법을 마련해주고, 인터페이스에 대한 대략적인 조정을 만들기 위해 특성을 사용하게 될 것입니다. 특성에 대한 대부분의 변경은 스토리보드 파일에서 직접적으로 다뤄질 수 있습니다. 추가적은 약간의 코드도 요구됩니다.

Configuring Your Storyboard to Handle Different Size Classes

인터페이스 빌더를 사용하면 다른 사이즈 클래스를 인터페이스에 적용하기 쉽습니다. 스토리보드 에디터는 다른 사이즈 클래스 설정으로 인터페이스를 표시하는 것, 특정 설정에서 뷰를 제거하는 것, 다른 레이아웃 제약을 구체화하는 것을 지원합니다. 다른 사이즈 클래스에 다른 이미지를 전달하는 이미지 에셋을 생성하는 것도 할 수 있습니다. 이 도구를 사용하는 것은 런타임에 코드 작성으로 같은 변경을 수행할 필요가 없다는 것을 의미합니다. 대신 UIKit은 현재 사이즈 클래스가 변경될 때, 자동으로인터페이스를 업데이트합니다.

Figure 13-1은 인터페이스 빌더에서 인터페이스를 설정하는 데 사용할 수 있는 도구들을 보여줍니다. 사이즈 클래스 viewing 컨트롤은 인터페이스의 모양을 변경시킵니다. 이 컨트롤을 사용함으로써 인터페이스가 어떻게 주어진 사이즈 클래스를 탐색할 수 있는지를 확인할 수 있습니다. 개별 뷰의 경우 주어진 사이즈 클래스에서 뷰가 제시되어야 하는지에 대한 여부를 설정하기 위해 installation 컨트롤을 사용하시기 바랍니다. 새 설정을 추가하려면 체크박스 왼쪽의 플러스 버튼을 사용하시기 바랍니다.

Figure 13-1 Customizing your interface for different size classes

NOTE
설지되지 않은 뷰는 뷰 계층구조에 남아있고, 정상적으로 조작될 수 있습니다. 하지만 스크린에 나타나지는 않습니다.

이미지 에셋은 앱의 이미지 리소스를 저장하기 위해 선호되는 방식입니다. 각 이미지 에셋은 같은 이미지의 여러 가지 버전을 포함합니다. 각 버전은 특정 설정을 위해 디자인되어 있습니다. 표준 디스플레이와 레티나 디스플레이를 위해 이미지를 다른 방식으로 구체화하는 것과 더불어 수평과 수직 사이즈 클래스에 각각 다른 이미지를 구체화할 수도 있습니다. 이미지 에셋이 설정될 때, UIImageView 객체는 자동으로 현재 사이즈 클래스와 레졸루션에 연관되는 이미지를 자동으로 선택합니다.

Figure 13-2는 이미지 에셋 특성을 보여줍니다. 넓이와 높이 특성을 변경하는 것은 카탈로그에 더 많은 이미지에 대한 슬롯을 추가합니다. 사이즈 클래스 조합 각각을 위해 사용하려는 이미지를 슬롯에 채우시기 바랍니다.

Figure 13-2 Configuring image assets for different size classes

Changing the Traits of a Child View Controller

자식 뷰 컨트롤러는 기본값으로 그들의 부모 뷰 컨트롤러의 특성을 상속받습니다. 사이즈 클래스와 같은 특성의 경우 각가의 자식이 부모의 특성과 같은 특성을 갖는 것은 이치에 맞지 않을 수 있습니다. 예를 들어 regular 환경에서 뷰 컨트롤러는 하나 혹은 하나 이상의 compact 사이즈 클래스를 자식에게 할당해 해당하는 자식의 줄어든 공간을 반영하길 원할 수 있습니다. 컨테이너 뷰 컨트롤러를 구현할 때, 컨테이너 뷰 컨트롤러의 setOverrideTraitCollection:forChildViewController: 메소드를 호출해 자식의 특성을 수정할 수 있습니다.

Listing 13-1은 새로운 특성 집합을 생성하는 방법과 자식 뷰 컨트롤러에 연관시킬 수 있는 방법을 보여줍니다. 부모 뷰 컨트롤러로부터 이 코드를 수행할 것이며, 한 번만 필요합니다. 오버라이드된 특성은 특성이 다시 변경되기까지 자식에네 남아있습니다. 혹은 뷰 컨트롤러 계층구조로부터 자식을 제거하기까지 남아있습니다.

Listing 13-1 Changing the traits of a child view controller

UITraitCollection* horizTrait = [UITraitCollection
                 traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
UITraitCollection* vertTrait = [UITraitCollection
                 traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassCompact];
UITraitCollection* childTraits = [UITraitCollection
                 traitCollectionWithTraitsFromCollections:@[horizTrait, vertTrait]];
 
[self setOverrideTraitCollection:childTraits forChildViewController:self.childViewControllers[0]];

부모 뷰 컨트롤러의 특성이 변경될 때, 자식은 부모에 의해 명시적으로 오버라이드되지 않은 모든 특성을 상속받습니다. 예를 들어 부모의 수평 사이즈 클래스가 regular에서 compact로 바뀔 때, 앞서 살펴본 예시의 자식은 여전히 regular 수평 사이즈 클래스로 남아있습니다. 그러나 displayScale 속성이 변경되면, 자식은 새로운 값을 상속받습니다.

Adapting Presented View Controllers to a New Style

제시된 뷰 컨트롤러는 수평 regular와 compact 환경 사이에 자동으로 적응합니다. 수평 regular에서 수평 compact 환경으로 전환이 이뤄질 때, UIKit은 기본값으로 내장된 프리젠테이션 스타일을 UIModalPresentationFullScreen로 변경합니다. 사용자 정의 프리젠테이션 스타일의 경우 프리젠테이션 컨트롤러는 적응 작동을 정의할 수 있고, 그에 따라 프리젠테이션을 조정할 수 있습니다.

어떤 앱의 경우 전체 스크린 스타일을 적용하는 것이 문제를 발생시킬 수 있습니다. 예를 들어 팝오버는 bounds의 바깥 부분을 탭하면 해제됩니다. 하지만 Figure 13-3에서 보이는 것처럼 팝오버가 전체 화면을 덮고 있는 compact 환경에서 그렇게 하는 것은 불가능합니다. 기본값 적응 스타일이 적합하지 않을 때, UIKit에게 다른 스타일을 사용하도록 하거나 전체 스크린 스타일에 더 적합한 완전히 다른 뷰 컨트롤러를 제시하도록 요청할 수 있습니다.

Figure 13-3 A popover in regular and compact environments

프리젠테이션 스타일에서 기본값 적응형 동작을 변경하려면 관련이 있는 프리젠테이션 컨트롤러에 딜리게이트를 할당해야 합니다. 뷰 컨트롤러의 presentationController 속성을 사용해 프리젠테이션 컨트롤러에 접근할 수 있습니다. 프리젠테이션 컨트롤러는 모든 적응 관련 변경사항을 만들기 전에 딜리게이트 객체를 참조합니다. 딜리게이트는 기본값과 다른 프리젠테이션 스타일을 반활할 수 있고, 표시하고자 하는 대안 뷰 컨트롤러를 갖는 프리젠테이션 컨트롤러를 제공할 수 있습니다.

기본값이 아닌 다른 프리젠테이션 스타일을 구체화하려면 딜리게이트의 adaptivePresentationStyleForPresentationController: 메소드를 사용해야 합니다. compact 환경으로 전환될 때, 지원되는 스타일은 두 전체 화면 스타일 혹은 UIModalPresentationNone 뿐입니다. UIModalPresentationNone을 반환하는 것은 프리젠테이션 컨트롤러에게 compact 환경을 무시하도록 요청하고, 이전 프리젠테이션 스타일을 계속해서 사용할 것을 요청합니다. 팝오버의 경우 변경사항을 무시하는 것은 모든 기기에 대해 아이패드와 같은 팝오버 작동을 제공합니다. Figure 13-4는 기본값 전체 스크린 적응과 조정이 없는 것을 양옆으로 각각 보여줌으로써 프리젠테이션의 결과를 비교할 수 있도록 해줍니다.

Figure 13-4 Changing the adaptive behavior for a presented view controller

뷰 컨트롤러를 완전히 교체하려면 딜리게이트의 presentationController:viewControllerForAdpativePresentationStyle: 메소드를 구현해야 합니다. compact 환경에 적응할 때, 뷰 계층구조에 네비게이션 컨트롤러를 삽입하기 위해 위 메소드를 사용할 것입니다. 혹은 작은 공간에 구체적으로 설계되었던 뷰 컨트롤러를 로드하기 위해 위 메소드를 사용하게 될 것입니다.

Tips for Implementing Adaptive Popovers

팝오버는 수평 regular에서 수평 compact로 바뀔 때 추가적인 수정이 필요합니다. 수평 compact에서 기본값 동작은 전체 화면 프리젠테이션으로 변경하기 위해 팝오버합니다. 팝오버는 팝오버의 bounds 바깥에 탭하면 해제되기 때문에 전체 스크린 프리젠테이션으로 변경하는 것은 팝오버를 해자히기 위한 주요 테크닉을 제거합니다. 아래에 보이는 것 중 한 가지를 수행함으로써 해당 동작을 보상할 수 있습니다.

  • 기존 네비게이션 스택에 팝오버의 뷰 컨트롤러를 넣습니다. 부모 네비게이션 컨트롤러가 사용이 가능한 경우 팝오버를 해제하고 팝오버의 뷰 컨트롤러를 네비게이션 스택에 넣어야 합니다.
  • 팝오버가 전체 스크린으로 제시될 때 팝오버 해제를 위한 컨트롤을 추가합니다. 팝오버의 뷰 컨트롤러에 대한 컨트롤을 추가할 수 있습니다. 더 나은 옵션은 팝오버를 네비게이션 컨트롤러로 교체하는 것입니다. 이는 presentationController:viewControllerForAdaptivePresentationStyle: 메소드를 사용해서 구현할 수 있습니다. 네비게이션 컨트롤러를 사용하는 것은 모달 인터페이스를 제공하고, 완료 버튼이나 컨텐트 해제를 위한 다른 컨트롤을 추가할 수 있는 공간을 제공합니다.
  • 모든 적응형 변경사항을 제거하기 위해 프리젠테이션 컨트롤러 딜리게이트를 사용합니다. 팝오버 프리젠테이션 컨트롤러를 가져오고, 여기에 adaptivePresentationStyleForPresentationController: 메소드를 구현하는 딜리게이트를 할당해야 합니다. 해당 메소드로부터 UIModalPresentationNone을 반환하는 것은 팝오버가 팝오버로써 표시되는 것을 계속할 수 있도록 합니다. 더 많은 정보는 Adapting Presented View Controllers to a New Style을 살펴보시기 바랍니다.

Adapting Presented View Controllers to a New Style은 윗부분에 등장합니다.

Responding to Size Changes

크기 변경은 여러 이유로발생할 수 있습니다. 아래 내용을 포함합니다.

  • 기본 윈도우의 차원이 변경되는 경우입니다. 보통 기기 회전 때문에 발생합니다.
  • 부모 뷰 컨트롤러가 자식 뷰 컨트롤러의 크기를 재조정하는 경우입니다.
  • 프리젠테이션 컨트롤러가 제시된 뷰 컨트롤러의 크기를 변경하는 경우입니다.

크기 변경이 발생하면 UIKit은 정상적인 레이아웃 프로세스를 통해 시각화된 뷰 컨트롤러의 크기와 위치를 자동으로 업데이트합니다. 오토 레이아웃 제약을 사용해 뷰의 크기와 위치를 구체화했었다면, 앱은 모든 크기 변경을 자동으로 적용하고, 앱은 다른 스크린 크기에서 작동해야 합니다.

원하는 모양을 달성하기에 오토 레이아웃 제약이 충분하지 않다면, viewWillTransitionToSize:withTransitionCoordinator: 메소드를 사용해 레이아웃을 변경할 수 있습니다. 크기 변경 애니메이션과 함게 동작ㅇ하는 추가적인 애니메이션을 생성하기 위해 위 메소드를 사용할 수도 있습니다. 예를 들어 인터페이스 회전이 일어나는 동안 인터페이스의 부분에 대해 카운터 로테이션 행렬을 생성하고자 전환 조정자의 targetTransform 속성을 사용할 수 있습니다.

0개의 댓글