Getting Started

Panther·2021년 5월 24일
0

Auto Layout Guide

목록 보기
1/5

Understanding Auto Layout

오토레이아웃은 모든 뷰에 잡아준 제약을 기준으로 뷰 계층구조 속에서 뷰의 크기와 위치를 동적으로 계산합니다. 예를 들어 버튼을 이미지뷰에 수평으로 중앙에 두고 버튼의 top edge를 항상 8포인트 떨어지게 두는 제약을 줄 수 있습니다. 만약 이미지뷰의 크기 혹은 위치가 변화하면 버튼의 위치는 그에 대응하게끔 자동으로 위치가 조정됩니다.

이러한 제약사항 기반의 접근방식은 내적, 외적 변화 모두에 동적으로 반응할 수 있는 UI를 설계하는 데 도움을 줍니다.

External Changes

슈퍼뷰의 크기 혹은 위치가 변할 때 외적 변화가 발생합니다. 각 변화에 대해 사용 가능한 공간을 최선으로 사용할 수 있도록 뷰 계층구조의 레이아웃을 업데이트해야 합니다. 아래 내용이 흔히 볼 수 있는 외적 변화에 해당합니다.

  • 사용자가 윈도우 크기를 재조절 (OS X)
  • 사용자가 아이패드에서 스플릿뷰 진입 혹은 스플릿뷰에서 이탈 (iOS)
  • 기기 회전 (iOS)
  • 활성화된 통화와 오디오 리코딩 바의 나타남 혹은 사라짐 (iOS)
  • 다른 크기의 클래스를 지원하는 것을 원할 때
  • 다른 스크린 크기를 지원하는 것을 원할 때

대부분의 이러한 변화는 런타임에 발생합니다. 그리고 앱으로부터 동적 반응이 이뤄지기를 요구합니다. 다른 스크린 크기를 지원하는 것과 같은 여러 경우는 다른 환경에 적응하는 앱을 보여줍니다. 스크린 크기를 통해서도 런타임에 변화하지 않는 경우 적응형 인터페이스를 생성하는 것을 통해 앱이 아이폰 4S, 아이폰 6 Plus, 혹은 아이패드에서도 같은 동작으로 이뤄지도록 할 수 있습니다. Auto Layout은 아이패드에서 슬라이드오버와 스플릿뷰를 지원하는 데 핵심 요소입니다.

Internal Changes

내적 변화는 UI에 있는 뷰 혹은 컨트롤의 크기가 변경될 때 발생합니다. 아래에 몇 가지 흔히 볼 수 있는 내적 변화가 있습니다.

  • 앱 변화에 의해 컨텐츠가 표시되는 경우
  • 앱이 Internationalization 지원하는 경우
  • 앱이 동적 타입을 지원하는 경우 (iOS)

앱의 컨텐트가 변화할 때 새로운 컨텐트는 이전 것과 다른 레이아웃을 요구할 것입니다. 이런 경우는 보통 텍스트 혹은 이미지를 표시하는 앱에서 발생합니다. 예를 들어 뉴스앱은 개별 뉴스 기사의 크기에 기초한 레이아웃의 조정이 필요할 것입니다. 유사하게 사진 콜라주는 넓은 범위에 걸쳐 이미지 크기와 aspect ratio를 다뤄야 할 것입니다.

Internationalization은 다른 언어, 지역 문화에 적응할 수 있도록 만들어주는 프로세스입니다. 이를 지원하는 앱의 레이아웃은 그에 맞게 다른 모양을 취하고 있어야 하고, 앱이 지원하는 모든 언어와 지역에 맞게 나타나야 합니다.

Internationalization은 레이아웃에 대한 세 가지 주요 효과를 갖습니다. 첫 번째로 다른 언어에 UI를 번역할 때, 레이블은 다른 크기의 여백을 요구합니다. 예를 들어 독일어는 보통 영어에 비해 더 많은 여백이 필요합니다. 일본어는 빈번하게 더 적은 것을 요구합니다.

두 번째는 날짜와 숫자를 나타내기 위해 사용되는 형식이 지역과 지역 사이에서 변화할 수 있으며, 심지어 언어가 달라지지 않더라도 지역에 따라 다르게 표시해야 할 수 있습니다. 이런 경우는 언어 변화에 비해 미세한 변화만을 갖고 있지만, UI는 크기에 따른 미세한 변화에 적응하는 것을 필요로 합니다.

세 번째로 언어가 변하는 것은 텍스트의 크기뿐만 아니라 레이아웃의 구조 역시 영향을 미칠 수 있습니다. 언어들마다 다른 레이아웃 방향을 가집니다. 예를 들어 영어는 왼쪽에서 오른쪽 방향의 레이아웃을 사용합니다. 그리고 아랍어 및 히브릭어는 오른쪽에서 왼쪽 방향의 레이아웃을 사용합니다. 보통 UI 요소들의 순서는 레이아웃 방향과 일치해야 합니다. 영어에서 버튼은 오른쪽 하단 모서리에 있지만, 아랍어는 왼쪽 아래에 위치해야 합니다.

마지막으로 iOS 앱이 동적 타입을 지원한다면, 사용자는 앱에서 사용되는 폰트 크기를 조절할 수 있어야 합니다. 이는 UI에서 모든 텍스트 요소의 높이와 넓이를 변화시킬 수 있습니다. 앱이 실행되는 동안 사용자가 폰트 크기를 바꾼다면, 폰트와 레이아웃 모두가 그에 맞게 적응해야 합니다.

Auto Layout Versus Frame-Based Layout

UI 레이아웃을 설정하는 것은 세 가지 접근 방식이 있습니다. 코드로 작성하는 것, 외적 변화에 반응하는 오토리사이징 마스크를 사용하는 것 혹은 오토 레이아웃을 사용하는 것으로 세 가지입니다.

전통적으로 앱은 뷰 계층구조 속에서 각각의 뷰에 대한 프레임을 코드로 세팅해 레이아웃을 설정했습니다. 프레임은 슈퍼뷰의 좌표 시스템 속에서 뷰의 시작 위치, 높이, 넓이를 정의했습니다.

UI의 레이아웃을 설정하기 위해서 뷰 계층구조 속에 모든 뷰의 크기와 위치를 계산해야만 했습니다. 그리고 만약 변화가 발생하면 영향을 받는 모든 뷰에 대해 프레임을 다시 계산해야 했습니다.

여러 방식으로 코드로 정의한 뷰의 프레임을 정의하는 것은 가장 높은 수준의 유연함과 강력함을 제공합니다. 변화가 발생했을 때 글자 그대로 원하는 모든 변화를 만들 수 있습니다. 그러나 모든 변화에 대해 다뤄야 하기 때문에 레이아웃의 디자인, 디버깅, 유지보수를 위한 엄청나게 많은 노력이 들어가게 됩니다. 진정한 적응형 UI를 만드는 것은 수십 배가 넘는 어려움을 증가시킵니다.

이와 같은 노력을 경감시키기 위해 오토리사이징 마스크를 사용할 수 있습니다. 오토리사이징 마스크는 슈퍼뷰의 프레임이 변화할 때 어떻게 뷰의 프레임을 변화시킬 것인지 결정합니다. 이는 외적 변화에 적응하는 레이아웃 생성을 간편하게 해줍니다.

그러나 오토리사이징 마스크는 상대적으로, 가능한 레이아웃의 작은 하위집합만을 지원합니다. 복잡한 UI는 코드로 작성한 변화를 통해 오토리사이징 마스크의 확장을 이뤄야 할 필요가 있습니다. 추가적으로 오토리사이징 마스크는 외적 변화에 대해서만 적응합니다. 내적 변화는 지원하지 않습니다.

오토리사이징 마스크는 코드로 작성하는 레이아웃에 대한 반복적인 개선일 뿐입니다. 그런데 오토 레이아웃은 완전히 새로운 패러다임을 표현합니다. 뷰의 프레임에 대해 생각하는 것 대신 관계를 생각하면 됩니다.

오토 레이아웃은 제약을 두는 것의 연속을 이용해 UI를 정의합니다. 제약은 보통 두 뷰 사이의 관계를 표현합니다. 그러면 오토 레이아웃은 제약에 기반해 각 뷰의 크기와 위치를 계산합니다. 이를 통해 레이아웃은 내적 변화와 외적 변화 모두에 동적으로 반응할 수 있습니다.

특정 행동을 생성하기 위한 여러 가지 제약을 디자인하기 위해 사용되는 로직은 절차적 혹은 객체지향 코드를 작성해 사용되는 로직과 매우 다릅니다. 다행히 오토 레이아웃을 숙련하는 것은 코드로 작성하는 것과 다를 것이 없습니다. 간단한 두 가지 단계가 있습니다. 첫 번째로 제약에 기반한 레이아웃에 있는 로직을 이해할 필요가 있습니다. 그리고 API를 학습할 필요가 있습니다. 코드로 작성하는 것을 배운다면 단계들을 성공적으로 수행할 수 있습니다. 오토 레이아웃도 마찬가지입니다.

이 가이드의 나머지 부분은 오토 레이아웃으로 쉽게 전환할 수 있도록 설계되어 있습니다. 'Auto Layout Without Constraints'장은 UI에서 지원되는 오토 레이아웃의 생성을 간단하게 할 수 있는 고수준 추상화를 설명합니다. 'Anatomy of a Constraint'장은 오토 레이아웃과 성공적으로 상호작용할 수 있도록 이해가 필요한 배경 이론을 제공합니다. 'Working with Constraints in Interface Builder'장은 오토 레이아웃을 설계하기 위한 도구를 설명하고, 'Programmatically Creating Constraints'장과 'Auto Layout Cookbook'장은 세부적인 API를 설명합니다. 마지막으로 'Auto Layout Cookbook'은 복잡성의 수준이 다른 여러 가지 샘플 레이아웃을 제공하고, 이를 바탕으로 공부할 수 있으며 프로젝트에 사용할 수 있게 됩니다. 'Debugging Auto Layout'은 무엇인가 잘못되었을 때 이를 고치기 위한 조언과 도구를 제공합니다.

Auto Layout Without Constraints

스택뷰는 복잡한 제약 없이 오토 레이아웃을 쉽게 할 수 있는 방법을 제공합니다. 하나의 스택뷰는 UI 요소들의 행 혹은 열을 정의합니다. 스택뷰는 이와 같은 속성에 기초해 요소들을 정렬합니다.

  • axis: (UIStackView만 해당) 스택뷰의 방향을 수직 혹은 수평으로 정의합니다.
  • orientation: (NStackView만 해당) 스택뷰의 오리엔테이션을 수직 혹은 수평으로 정의합니다.
  • distribution: 축에 따라 뷰의 레이아웃을 정의합니다.
  • alignment: 스택뷰의 축에 수직인 뷰의 레이아웃을 정의합니다.
  • spacing: 인접한 뷰 사이의 여백을 정의합니다.

스택뷰를 사용하려면 캔버스에 인터페이스 빌더에서 수직 혹은 수평 스택뷰를 드래그합니다. 그리고 컨텐트를 스택에 드래그 앤 드랍으로 넣습니다.

만약 객체에 고유한 컨텐트 크기를 갖고 있다면, 그 컨텐트는 그에 해당하는 크기로 스택에 나타날 것입니다. 컨텐트 크기가 고유한 값으로 있지 않다면, 인터페이스 빌더는 디폴트 크기를 제공합니다. 객체의 크기를 조절할 수 있고, 인터페이스 빌더는 크기를 유지하기 위해 제약을 추가할 수 있습니다.

더 정교하게 레이아웃을 설정하려면 어트리뷰트 인스펙터를 사용해 스택뷰의 속성을 수정할 수 있습니다. 예를 들어 아래 예제는 8포인트 여백과 Fiils Equally distribution을 사용합니다.

스택뷰는 정렬된 뷰의 content-hugging과 compression-resistance의 우선순위에 따라 레이아웃을 기반으로 합니다. 사이즈 인스펙터를 사용해 수정할 수 있습니다.

Note
정렬된 뷰들에 직접 제약을 추가해 레이아웃을 수정할 수 있습니다. 그러나 충돌은 피하길 원할 것입니다. 일반적으로 만약 뷰의 크기가 주어진 차원에 대해 고유한 컨텐트 사이즈로 디폴트 설정되는 경우, 그에 해당하는 차원에 대한 제약을 안전하게 추가할 수 있습니다. 제약에 해당하는 충돌에 대한 더 많은 정보가 필요하다면 Unsatisfiable Layouts를 살펴보시기 바랍니다.

Unsatisfiable Layouts는 이후 소개할 'Debugging Auto Layout'에 등장합니다.

추가적으로 더 복잡한 레이아웃을 설정해 스택뷰 내부에 스택뷰를 넣는 중첩 스택뷰를 사용할 수 있습니다.

레이아웃 관리를 감당할 수 있는 선에서 스택뷰를 사용하시길 권장합니다. 스택뷰만으로 원하는 목적을 달성할 수 없을 때에만 제약을 생성하시길 권장합니다.

스택뷰를 사용하는 것에 대해 더 많은 정보가 필요하시다면 UIStackView Class Reference 혹은 NSStackView Class Reference를 살펴보시기 바랍니다.

UIStackView Class Reference
https://developer.apple.com/documentation/uikit/uistackview

NSStackView Class Reference
https://developer.apple.com/documentation/appkit/nsstackview

Note
중첩 스택뷰의 창의적인 사용은 복잡한 UI를 제공할 수 있지만, 제약을 두는 것을 피할 수는 없습니다. 최소한 가장 바깥쪽 스택의 위치(그리고 아마도 크기)를 정의할 필요가 있을 것입니다.

Anatomy of a Constraint

뷰 계층구조에서 레이아웃은 선형적인 수식의 연속으로써 정의됩니다. 각각의 제약은 하나의 수식을 표현합니다. 하나의 가능한 솔루션을 갖고 있는 수식들의 연속을 선언하는 것이 목적입니다.

샘플 수식은 아래와 같습니다.

이 제약은 빨간색 뷰의 leading edge는 파란색 뷰의 trailing edge 이후 8포인트여야만 한다는 것을 보여줍니다. 이 수식은 몇 가지 부분으로 구성되어 있습니다.

  • Item 1. 이 경우 수식에서 첫 번째 아이템은 빨간색 뷰입니다. 이 아이템은 뷰이거나 레이아웃 가이드가 되어야 합니다.

  • Attribute 1. 첫 번째 아이템에 대한 제약이 주어질 특성은 빨간색 뷰의 leading edge입니다.

  • Relationship. 왼쪽과 오른쪽 사이의 관계입니다. 관계는 세 가지 중 한 가지가 될 수 있습니다. equal, greater than or equal, less than or equal 세 가지 중 한 가지입니다. 이 경우 왼쪽과 오른쪽이 같습니다.

  • Multiplier. 두 번째 특성은 소수점을 갖는 숫자에 의해 곱해지는 것입니다. 이 경우 멀티플라이어는 1.0입니다.

  • Item 2. 수식에서 두 번째 아이템은 파란색 뷰입니다. 첫 번째 아이템과 다르게 왼쪽이 비어있습니다.

  • Attribute 2. 두 번째 아이템에 대해 제약을 두고 있는 특성은 파란색 뷰의 trailing edge입니다. 만약 두 번째 아이템의 왼쪽이 비어 있다면, 이는 특성이 되어서는 안 됩니다.

  • Constant. 상수에 해당하는 부분은 8.0입니다. 특성2에 더해집니다.

대부분의 제약은 UI에서 두 가지 아이템 사이의 관계를 정의합니다. 이런 아이템들은 뷰 혹은 레이아웃 가이드를 나타내고 있습니다. 제약은 하나의 아이템이 갖는 두 가지 다른 특성의 관계를 정의할 수도 있습니다. 예를 들어 아이템의 높이와 넓이를 aspect ratio로 설정할 수 있습니다. 아이템의 높이와 넓이에 상수를 할당할 수도 있습니다. 상수로 작업할 때 두 번째 아이템은 비워두고, 두 번째 특성은 Not An Attribute로 설정되며, 멀티플라이어는 0으로 설정됩니다.

Auto Layout Attributes

Auto Layout에서 특성은 제약이 가해질 수 있는 모양으로 정의합니다. 보통 이는 네 가지 edge(leading, trailing, top, bottom)를 포함합니다. 또한, 높이와 넓이, 수직 중앙과 수평 중앙이 있습니다. 텍스트 아이템은 하나 혹은 하나 이상으로 설정될 수 있는 베이스라인 특성을 갖고 있습니다.

전체 특성 목록은 NSLayoutAttribute 열거형을 살펴보시기 바랍니다.

NSLayout Attribute
https://developer.apple.com/documentation/uikit/nslayoutattribute

Note
OS X와 iOS는 NSLayoutAttribute 열거형을 사용하고 있지만, 값의 설정을 정의하는 데 둘 사이에 미묘한 차이가 있습니다. 특성의 전체 리스트를 정확히 보려면, 원하는 플랫폼의 자료인지 정확하게 확인하시고 보시기 바랍니다.

Sample Equations

이와 같은 수식에 이용 가능한 많은 파라미터와 특성은 여러 가지 다양한 타입의 제약을 생성할 수 있게 도와줍니다. 뷰 사이의 여백, 뷰의 edge를 정렬을 정의할 수 있고, 두 뷰의 크기를 상대적 크기로 결정할 수 있으며, 심지어 뷰의 aspect ratio를 결정할 수 있습니다. 그러나 모든 특성이 호환 가능한 것은 아닙니다.

특성들은 두 가지 기본적인 타입이 있습니다. 크기 특성(예를 들어 높이와 넓이)과 위치 특성(예를 들어 leading, left, top)이 있습니다. 크기 특성은 위치를 표시하는 것 없이 하나의 아이템이 얼마나 큰지를 구체화하는 데 사용됩니다. 위치 특성은 다른 무엇을 기준으로 한 아이템의 위치를 구체화하는 데 사용됩니다. 그러나 아이템의 크기를 표시하지는 않습니다.

이와 같은 차이점을 염두에 두고 아래 규칙들이 적용됩니다.

  • 크기 특성을 위치 특성에 대해 제약을 둘 수 없습니다.
  • 위치 특성에 상수값을 할당할 수 없습니다.
  • 위치 특성에는 식별이 불가능한 멀티플라이어(1.0 외의 값)를 사용할 수 없습니다.
  • 위치 특성에서 수평 특성에 대해 수직 특성을 적용할 수 없습니다.
  • 위치 특성에서 left 혹은 right 특성에 leading 혹은 trailing 특성으로 제약을 둘 수 없습니다.

예를 들어 하나의 아이템에 Top으로 상수값 20.0을 설정하는 것 자체는 추가적인 컨텍스트 없이 의미를 갖지 못합니다. 아이템의 위치 특성을 다른 아이템의 관계와 관련해서 정의해야 합니다. 예를 들어 슈퍼뷰의 Top에 20.0의 상수값을 설정할 수 있습니다. 그러나 한 아이템의 높이를 20.0으로 설정하는 것은 완전하게 유효합니다. 더 많은 정보를 살펴보려면 Interpreting Values를 살펴보시기 바랍니다.

Interpreting Values는 이 글의 더 아래에 소개됩니다.

Listing 3-1은 일반적인 제약의 다양한 형태를 담는 샘플 수식들을 보여줍니다.

Note
이 장에서 모든 예제 수식은 의사코드(pseudocode)를 사용해 표현되고 있습니다. 실제로 적용이 가능한 코드를 살펴보려면 Programmatically Creating Constraints or Auto Layout Cookbook을 살펴보시기 바랍니다.

Programmatically Creating Constraints는 더 이후에 나올 Advanced Auto Layout에서 소개되며, Auto Layout Cookbook은 이 글의 다음 글에 등장합니다.

Listing 3-1Sample equations for common constraints

// Setting a constant height
View.height = 0.0 * NotAnAttribute + 40.0
 
// Setting a fixed distance between two buttons
Button_2.leading = 1.0 * Button_1.trailing + 8.0
 
// Aligning the leading edge of two buttons
Button_1.leading = 1.0 * Button_2.leading + 0.0
 
// Give two buttons the same width
Button_1.width = 1.0 * Button_2.width + 0.0
 
// Center a view in its superview
View.centerX = 1.0 * Superview.centerX + 0.0
View.centerY = 1.0 * Superview.centerY + 0.0
 
// Give a view a constant aspect ratio
View.height = 2.0 * View.width + 0.0

Equality, Not Assignment

중요한 점은 NOTE에 표현된 수식은 등식이며 할당이 아니라는 점입니다.

오토 레이아웃이 이와 같은 수식들을 해결할 때, 단순히 왼쪽에 오른쪽 측면의 값을 할당하는 것이 아닙니다. 대신 특성1과 특성2의 관계가 참으로 만들 수 있도록 계산합니다. 이는 수식에서 아이템들의 순서를 자유롭게 바꿀 수 있음을 의미합니다. 예를 들어 Listing 3-2에 있는 수식들은 NOTE에 있는 수식들과 동일합니다.

Listing 3-2Inverted equations

// Setting a fixed distance between two buttons
Button_1.trailing = 1.0 * Button_2.leading - 8.0
 
// Aligning the leading edge of two buttons
Button_2.leading = 1.0 * Button_1.leading + 0.0
 
// Give two buttons the same width
Button_2.width = 1.0 * Button.width + 0.0
 
// Center a view in its superview
Superview.centerX = 1.0 * View.centerX + 0.0
Superview.centerY = 1.0 * View.centerY + 0.0
 
// Give a view a constant aspect ratio
View.width = 0.5 * View.height + 0.0

Note
아이템들의 순서를 바꿀 때는 멀티플라이어와 상수를 확실하게 역전시켜줘야 합니다. 예를 들어 상수 8.0은 -8.0이 됩니다. 멀티플라이어인 2.0은 0.5가 됩니다. 상수 0.0과 멀티플라이어 1.0은 바꾸지 않고 그대로 둡니다.

오토 레이아웃은 같은 문제를 해결하기 위해 다양한 방식을 제공한다는 것을 파악하셨을 것입니다. 표현하고자 하는 의도에 맞게 명확하게 표현할 수 있는 솔루션을 선택해야 합니다. 그러나 서로 다른 개발자들이라 하더라도 의심하지 않고 어떤 솔루션이 최고인지에 대해 논의하는 것은 의미가 없다고 생각할 것입니다. 접근 방식을 확실하게 정해놓고 그 방식을 고수한다면 장기적으로 문제를 덜 겪게 될 것입니다. 예를 들어 이 가이드는 아래와 같은 방식을 사용합니다.

1 분수 멀티플라이어보다 정수 멀티플라이어를 선호합니다.

2 음수인 상수보다 양수인 상수를 더 선호합니다.

3 가능한 경우 뷰의 레이아웃을 순서대로 표현합니다. (leading에서 trailing으로, top에서 bottom으로)

Creating Nonambiguous, Satisfiable Layouts

오토 레이아웃을 사용할 때, 목표는 가능한 솔루션이 하나인 일련의 수식을 제공하는 것입니다. 모호한 제약에는 하나 이상의 솔루션이 있습니다. 조건을 충족할 수 없는 제약은 유효한 솔루션이 없습니다.

일반적으로 제약은 각 뷰의 크기와 위치를 모드 정의해야합니다. 슈퍼뷰의 크기는 이미 설정(예를 들어 iOS에 있는 씬의 루트뷰)되어 있다고 가정하고, 모호하지 않고 조건을 충족하는 레이아웃은 차원 하나당 뷰 하나에 두 가지 제약이 필요합니다(슈퍼뷰는 개수에 포함시키지 않습니다). 그러나 사용하길 원하는 어떤 제약들과 관련해 선택할 수 있는 사항이 여러 가지 있습니다. 예를 들어 아래 세 가지 레이아웃 모두가 모호하지 않고 조건을 충족하는 레이아웃을 제공합니다(수평 제약만을 보여줍니다).

  • 첫 번째 레이아웃은 슈퍼뷰 leading edge를 기준으로 뷰의 leading edge에 제약을 둡니다. 뷰의 넓이를 고정값을 주기도 합니다. trailing edge의 위치는 슈퍼뷰의 크기와 다른 제약들에 기반해 계산됩니다.

  • 두 번째 레이아웃은 슈퍼뷰의 leading edge를 기준으로 뷰의 leading edge에 제약을 둡니다. 또한, 슈퍼뷰의 trailing edge를 기준으로 뷰의 trailing edge에 제약을 두고 있습니다. 뷰의 넓이는 슈퍼뷰의 크기와 다른 제약들에 기반해 계산됩니다.

  • 세 번째 레이아웃은 슈퍼뷰의 leading edge를 기준으로 뷰의 leading edge에 제약을 둡니다. 또한, 뷰와 슈퍼뷰에 중앙 정렬을 두고 있습니다. 넓이와 trailing edge의 위치 모두는 슈퍼뷰의 크기와 다른 제약들에 기반해 계산됩니다.

각 레이아웃은 하나의 뷰와 두 개의 수평 제약을 갖고 있음을 알아야 합니다. 각각의 경우에서 제약들은 넓이와 뷰의 수평 위치 모두를 완전하게 정의하고 있습니다. 이는 모든 레이아웃이 수평축에 따라 모호하지 않고 조건을 충족하는 레이아웃임을 의미합니다. 그러나 이 레이아웃들은 유용하지 않습니다. 뷰의 넓이가 바뀔 때 어떤 일이 발생하는지 생각해봐야 합니다.

첫 번째 레이아웃에서 뷰의 넓이는 변하지 않습니다. 대부분의 경우에서 뷰의 넓이가 변하지 않는 것은 원하는 바가 아닐 것입니다. 사실 일반적으로 뷰에 고정값을 할당하는 것은 피하는 것이 좋습니다. 오토 레이아웃은 환경에 동적으로 적응할 수 있는 레아이웃을 생성하기 위해 설계되었습니다. 뷰에 고정값을 설정할 때마다 환경에 동적으로 적응하는 기능을 포기하게 됩니다.

이는 명확하지 않습지만 두 번째와 세 번째 레이아웃은 동일한 움직임을 제공합니다. 두 가지는 슈퍼뷰의 넓이가 변화할 때 뷰와 슈퍼뷰 사이의 고정된 마진을 유지하고 있습니다. 그러나 그들은 동일할 필요는 없습니다. 일반적으로 두 번째 예시가 이해하기 쉽지만 세 번째 예시가 더 유용합니다. 특히 몇 가지 아이템들을 중앙으로 정렬할 때 그렇습니다. 항상 특정 레이아웃에 접근할 때 최선의 접근법을 선택해야 합니다.

조금 더 복잡한 경우를 생각해보려고 합니다. 아이폰에 두 뷰를 나란히 놓는다고 생각해보겠습니다. 모든 방향에 좋은 마진을 설정해야 하고 항상 같은 넓이를 갖고 있어야 합니다. 그리고 기기의 회전에 따라 크기가 재조정되어야 합니다.

아래에 가로, 세로 오리엔테이션에 따른 뷰들을 보여주고 있습니다.

이런 제약이 어떤 모습이어야 하는지를 아래에서 보여주고 있습니다.

위 솔루션은 아래처럼 나타낼 수 있습니다.

// Vertical Constraints
Red.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Red.bottom + 20.0
Blue.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Blue.bottom + 20.0
 
// Horizontal Constraints
Red.leading = 1.0 * Superview.leading + 20.0
Blue.leading = 1.0 * Red.trailing + 8.0
Superview.trailing = 1.0 * Blue.trailing + 20.0
Red.width = 1.0 * Blue.width + 0.0

레이아웃은 두 뷰를 갖고 있고 네 가지 수평 제약과 네 가지 수직 제약을 갖고 있습니다. 이는 오류가 없지는 않지만 맞는 방향으로 가고 있다는 것을 보여줍니다. 더 중요한 점은 모호하지 않고 조건을 충족할 수 잇는 레이아웃을 제공하면서, 제약들은 고유하게 모든 뷰들의 크기와 위치를 구체화하고 있습니다. 이러한 제약을 지우면 레이아웃은 모호해집니다. 추가적인 제약을 추가하면, 충돌이 발생할 위험이 있습니다.

이것이 유일한 솔루션은 아닙니다. 동일하게 유효한 접근방식이 아래에 있습니다.

슈퍼뷰에 대해 파란색 박스의 top과 bottom을 고정으로 두는 대신, 빨간 박스의 top에 파란 박스의 top을 정렬시킬 수 있습니다. 유사하게 빨간 박스의 bottom에 파란 박스의 bottom을 정렬시킬 수 있습니다. 제약은 아래와 같이 표현할 수 있습니다.

// Vertical Constraints
Red.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Red.bottom + 20.0
Red.top = 1.0 * Blue.top + 0.0
Red.bottom = 1.0 * Blue.bottom + 0.0
 
//Horizontal Constraints
Red.leading = 1.0 * Superview.leading + 20.0
Blue.leading = 1.0 * Red.trailing + 8.0
Superview.trailing = 1.0 * Blue.trailing + 20.0
Red.width = 1.0 * Blue.width + 0.0

예시는 아직 두 뷰를 갖고 있고 네 가지 수평 제약과 네 가지 수직 제약을 갖고 있습니다. 여전히 모호하지 않고 조건을 충족하는 레이아웃을 제공하고 있습니다.

BUT WHICH IS BETTER?
위의 모든 솔루션이 유효한 레이아웃을 제공합니다. 어떤 것이 더 나은지를 생각해봐야 합니다.

안타깝게도 한 가지 방법이 다른 한 가지 방법보다 우월하다는 것을 객관적으로 검증하는 것은 불가능합니다. 각각읜 고유한 강점과 약점을 갖습니다.

첫 번째 솔루션이 뷰가 지워졌다고 가정할 때 더 강력합니다. 뷰 계층구조로부터 뷰를 제거하는 것은 그 뷰를 참조하고 있는 모든 제약 또한 지워집니다. 그렇기 때문에 만약 빨간색 뷰를 지우면 파란색 뷰는 위치를 잡는 세 가지 제약이 남아있게 됩니다. 레이아웃이 다시 유효하려면 하나의 제약만 추가하면 됩니다. 두 번째 솔루션에서 빨간색 뷰를 제거하는 것은 파란색 뷰의 제약 중 한 가지만을 남기게 될 것입니다.

반면에 첫 번째 솔루션에서 만약 View의 top과 bottom을 정렬하려고 한다면, top과 bottom 제약에 같은 값을 넣어야 할 것입니다. 하나의 값을 변경하면 다른 하나를 변경해야 합니다.

Constraint Inequalities

지금까지 모든 샘플들은 equality로 제약을 두는 것을 보여줬습니다. 그러나 이는 일부분일 뿐입니다. 제약들은 inequality로 표현할 수도 있습니다. 구체적으로 제약의 관계는 equal to, greater than or equal to, less than or equal to가 될 수 있습니다.

예를 들어 뷰의 크기에 대해 최소, 최대 크기로 정의하기 위한 제약을 사용할 수도 있습니다.

Listing 3-3Assigning a minimum and maximum size

// Setting the minimum width
View.width >= 0.0 * NotAnAttribute + 40.0
 
// Setting the maximum width
View.width <= 0.0 * NotAnAttribute + 280.0

inequality를 사용하는 순간에 하나의 차원당 뷰의 두 제약들의 규칙은 무너집니다. 언제든지 두 가지 종류의 inequality를 하나의 equality 관계로 대체할 수 있습니다. Listing 3-4에서 보이는 것처럼 하나의 equal 관계와 한 쌍의 inequality는 같은 움직임을 제공합니다.

Listing 3-4Replacing a single equal relationship with two inequalities

// A single equal relationship
Blue.leading = 1.0 * Red.trailing + 8.0
 
// Can be replaced with two inequality relationships
Blue.leading >= 1.0 * Red.trailing + 8.0
Blue.leading <= 1.0 * Red.trailing + 8.0

역의 관계가 항상 성립하지는 않습니다. 왜냐하면 두 inequality는 하나의 equal 관계와 항상 동등하지는 않기 때문입니다. 예를 들어 Listing 303의 inequality 두 가지는 뷰의 넓이를 가능한 값들의 영역에 제한을 두고 있지만, 스스로 넓이를 정의하고 있지는 않습니다. 이 범위 안에서 뷰의 위치와 크기를 결정하기 위한 수평 제약을 추가하는 것이 필요합니다.

Constraint Priorities

기본값으로 모든 제약들은 필요합니다. 오토 레이아웃은 모든 제약을 충족하는 솔루션을 계산해야만 합니다. 만약 그렇지 않으면 에러가 발생합니다. 오토 레이아웃은 조건을 충족하지 못하는 제약들에 대한 정보를 콘솔에 표시하고, 이를 깨기 위한 제약의 한 가지를 선택합니다. 그러면 깨진 제약 없이 솔루션을 다시 계산합니다. 더 많은 정보가 필요하다면 Unsatisfiable Layouts를 살펴보시기 바랍니다.

Unsatisfiable Layouts는 다다음 글인 Debugging Auto Layout에 나옵니다.

선택적인 제약을 생성할 수도 있습니다. 모든 제약들은 1과 1000 사이의 우선순위를 가집니다. 1000의 우선순위를 갖는 제약들이 요구됩니다. 다른 모든 제약들은 선택적입니다.

솔루션을 계산할 때, 오토 레이아웃은 가장 높은 우선순위에서 가장 낮은 우선순위를 순서로 모든 제약들이 충족하도록 시도합니다. 선택적인 제약을 충족시킬 수 없다면, 그 제약을 건너뛰고 다음 제약을 계속해서 계산합니다.

선택적 제약이 충족될 수 없다고 하더라도 이는 여전히 레이아웃에 영향을 줍니다. 만약 제약을 건너뛴 이후에도 레이아웃에 어떠한 모호성이 존재한다면, 시스템은 제약에 가장 가까운 솔루션을 선택합니다. 이와 같은 방식으로 충족할 수 없는 선택적 제약은 뷰를 끌어내는 역할을 하게 됩니다.

선택적 제약들과 inequality는 보통 함께 작동합니다. 예를 들어 Listing 3-4에서 두 가지 ineqaulity에 다른 운선순위를 제공할 수 있습니다. greater-than-or-equal 관계는 우선순위가 1000으로 요구되고, less-than-or-equal 관계는 우선순위가 더 낮은 250으로 요구될 수 있습니다. 이는 파란색 뷰가 빨간색 뷰로부터 8.0 포인트 이상 가까워질 수 없음을 의미합니다. 그러나 다른 제약들은 멀리 끌어낼 수 있습니다. 여전히 선택적 제약은 파란색 뷰를 빨간색 뷰의 방향으로 끌어당기고 있고, 이는 레이아웃에서 주어진 다른 제약들과 함께 두 뷰 사이가 가능한 8.0 포인트 여백만큼 가까울 수 있다는 것을 보장하면서 이뤄집니다.

Note
1000의 우선순위 값을 모두에 사용해야한다고 생각하지 않아도 됩니다. 사실 우선순위들은 시스템이 결정한 low (250), medium (500), high(750), required (1000) 우선순위를 중심으로 클러스터링되어야 합니다. 같은 우선순위를 방지하기 위해 1에서 2포인트 높거나 낮은 제약 조건을 만들어야 할 수도 있습니다. 만약 그 이상으로 나아가고 있다면, 레이아웃 로직을 재검토하길 원할 것입니다.

iOS에서 사전에 정의된 상수들의 리스트를 확인하고 싶으시다면 UILayoutPriority 열거형을 살펴보시기 바랍니다. OS X에서는 Layout Priorities 상수들을 살펴보시기 바랍니다.

UILayoutPriority
https://developer.apple.com/documentation/uikit/uilayoutpriority

Intrinsic Content Size

지금까지 모든 예제는 뷰의 위치와 크기를 결정하기 위한 제약들을 사용해왔습니다. 그러나 몇 가지 뷰는 현재 컨텐트가 주어진 상태에서 자연스럽게 크기를 갖습니다. 이를 내재된 컨텐트 크기라고 합니다. 예를 들어 버튼의 내재된 컨텐트 크기는 타이틀과 작은 마진을 합한 크기입니다.

모든 뷰가 내재된 컨텐트 크기를 갖고 있지는 않습니다. 뷰가 내재된 컨텐트 크기를 갖는 경우 내재된 컨텐트 크기를 뷰의 높이, 넓이 혹은 둘 모두를 정의할 수 있습니다. Table 3-1에 몇 가지 예제가 있습니다.

Table 3-1Intrinsic content size for common controls

ViewIntrinsic content size
UIView와 NSView고유한 크기가 없습니다.
슬라이더슬라이더의 타입에 대해 넓이와 높이 혹은 둘 모두를 정의해야 합니다. (OS X).
레이블, 버튼, 스위치, 텍스트 필드높이와 넓이 모두를 정의해야 합니다.
텍스트뷰와 이미지뷰내재된 컨텐트 크기는 다양할 수 있습니다.

내재된 컨텐트 크기는 뷰의 현재 컨텐트를 기반으로 합니다. 레이블 혹은 버튼의 내재된 컨텐트 크기는 텍스트가 보여지는 양과 사용하는 폰트를 기반으로 합니다. 다른 뷰들은 더 복잡합니다. 예를 들어 비어있는 이미지뷰는 내재된 컨텐트 크기를 갖지 않습니다. 이미지를 넣는 순간 내재된 컨텐트 크기는 이미지의 크기로 설정됩니다.

텍스트뷰의 내재된 컨텐트 크기는 컨텐트에 따라 달라집니다. 스크롤링이 가능한지 아닌지, 그리고 뷰에 대해 다른 제약이 적용되었는지에 따라 달라집니다. 예를 들어 스크롤링이 가능한 경우 뷰는 내재된 컨텐트를 갖지 않습니다. 스크롤링이 가능하지 않는 경우라면 내재된 컨텐트 크기는 기본값으로 선 래핑(line wrapping) 없이 텍스트의 크기에 기반해 계산됩니다. 예를 들어 만약 텍스트에 반환되는 값이 없다면, 단일 라인 텍스트로써 컨텐트의 레이아웃을 설정하기 위해 필요한 높이와 넓이를 계산합니다. 만약 뷰의 넓이를 구체화하는 제약을 추가한다면, 내재된 컨텐트 크기는 주어진 넓이 안에서 텍스트를 표시하기 위해 필요한 만큼의 높이를 결정합니다.

오토 레이아웃은 각 차원에 대해 한 쌍의 제약을 사용하면서 뷰의 내재된 컨텐트 크기를 표현합니다. content hugging은 뷰를 안쪽으로 당겨와 컨텐트 주변에 잘 맞도록합니다. compression resistance는 컨텐트를 자르지 않도록 바깥방향으로 밀어냅니다.

이와 같은 제약들은 Listing 3-5에서 살펴봤었던 inequality를 사용하면서 정의됩니다. InstrinsicHeight과 InstrinsicWidth 상수는 뷰의 내재된 컨텐트 크기로부터 높이와 넓이를 나타냅니다..

Listing 3-5Compression-Resistance and Content-Hugging equations

// Compression Resistance
View.height >= 0.0 * NotAnAttribute + IntrinsicHeight
View.width >= 0.0 * NotAnAttribute + IntrinsicWidth
 
// Content Hugging
View.height <= 0.0 * NotAnAttribute + IntrinsicHeight
View.width <= 0.0 * NotAnAttribute + IntrinsicWidth

각각의 제약들은 스스로의 우선순위를 가질 수 있습니다. 기본값으로 뷰들은 content hugging에 대해 250 값의 우선순위를 사용하고, compression resistance는 750 값의 우선순위를 가집니다. 그러므로 뷰를 축소하는 것보다 늘리는 것이 더 쉽습니다. 대부분의 컨트롤에서 위와 같이 하는 것이 바람직합니다. 예를 들어 버튼을 내재된 컨텐트 값보다 더 크도록 안전한 방법으로 늘릴 수 있습니다. 그러나 이를 줄이면 컨텐트는 아마 잘릴 것입니다. 인터페이스 빌더는 때때로 이를 방지하기 위해 우선순위를 바꿉니다. 더 많은 정보가 필요하다면 Setting Content-Hugging and Compression-Resistance Priorities를 살펴보시기 바랍니다.

Setting Content-Hugging and Compression-Resistance Priorities는 이 글의 뒷부분에 등장합니다.

가능하다면 레이아웃에서 뷰의 내재된 컨텐트 크기를 사용하는 것이 좋습니다. 이렇게 해야 뷰의 컨텐트가 변화할 때 레이아웃은 그에 맞게 동적으로 적응할 수 있도록 해줄 것입니다. 그리고 명확하고 충돌없는 레이아웃을 생성하기 위해 필요한 제약의 수도 줄여줍니다. 물론 뷰의 content-hugging과 compression-resistance 우선순위를 다룰 필요가 있긴 합니다. 아래에 내재된 컨텐트 크기를 다루기 위한 몇 가지 가이드라인이 있습니다.

  • 여백을 채우기 위해 연달아 있는 뷰들을 늘릴 때, 만약 모든 뷰가 동일한 content-hugging 우선순위를 갖는다면 레이아웃은 모호해집니다. Auto Layout은 어떤 뷰를 늘려야 하는지 파악하지 못합니다.

    일반적인 예제는 레이블, 텍스트필드의 한 쌍입니다. 보통 텍스트필드를 늘려 남은 여백을 채우려고하면서 레이블은 내재된 컨텐트 크기를 갖도록 하려고 할 것입니다. 이를 확실하게 하려면 텍스트필드의 수평 content-hugging 우선순위를 레이블보다 낮은 값으로 해줘야 합니다.

    사실 이 예시는 인터페이스 빌더가 자동으로 다뤄주는 예시이긴 합니다. 모든 레이블의 content-hugging 우선순위를 251로 설정하면서 이뤄집니다. 만약 코드를 사용해 레이아웃을 생성한다면 content-hugging 우선순위를 직접 수정해야 합니다.

  • 투명한 배경(버튼 혹은 레이블처럼)이 우연히 그들의 내재된 컨텐트 크기를 넘어서서 늘어난다면 이상하고 예상할 수 없는 레이아웃이 발생합니다. 실질적인 문제는 명확하지 않을 것입니다. 왜냐하면 텍스트가 의도하지 않는 위치에 나타나기 때문입니다. 의도하지 않는 늘어남을 방지하기 위해 content-hugging 우선순위를 증가시켜야 합니다.

  • 베이스라인 제약은 뷰의 내재된 컨텐트 높이에 있는 뷰에서만 작동합니다. 만약 한 뷰가 수직으로 늘어나거나 압축된다면 베이스라인 제약은 더이상 적절하게 정렬되지 않습니다.

  • 스위치와 같은 몇 가지 뷰들은 그들의 내재된 컨텐트 사이즈에서만 표시되어야 합니다. 늘어나거나 혹은 압축되는 것을 방지하기 위해 content-hugging과 compression-resistance 우선순위를 증가시켜야 합니다.

  • 뷰들에 필요한 content-hugging과 compression-resistance 우선순위를 직접 설정하는 것은 피하는 것이 좋습니다. 뷰의 크기가 잘못되는 것이 충돌을 일으키는 것보다 낫습니다. 만약 하나의 뷰가 하앙 내재된 컨텐트 크기를 가져야 한다면 999와 같은 높은 우선순위를 사용하시길 권장합니다. 이 접근법은 보통 늘어나거나 압축되는 것을 막아주고, 기대한 것보다 크거나 작은 환경 하에 표시되는 경우를 대비해 emergency pressure valve를 제공합니다.

Intrinsic Content Size Versus Fitting Size

내재된 컨텐트 크기는 오토 레이아웃에서 입력(input)의 역할을 합니다. 뷰가 내재된 컨텐트 크기를 갖고 있으면 시스템은 크기를 표현하는 제약을 생성하고 제약들은 레이아웃을 계산하기 위해 사용됩니다.

반면에 fitting size는 오토 레이아웃 엔진으로부터 출력이 됩니다. 뷰의 제약에 기반해 계산되는 크기입니다. 만약 뷰가 오토 레이아웃을 사용해 하위뷰를 갖는다면, 시스템은 컨텐트에 기반해 뷰에 대한 fitting size를 계산할 수 있을 것입니다.

스택뷰가 좋은 예시입니다. 다른 제약을 제외하고, 시스템은 스택뷰가 갖는 컨텐트와 특성들에 기반해 스택뷰의 크기를 계산합니다. 여러 가지 방법으로 스택뷰는 내재된 컨텐트 크기를 갖는 것처럼 행동합니다. 단지 위치를 정의하기 위한 하나의 수직 제약과 수평 제약을 사용해 유효한 레이아웃을 생성할 수 있습니다. 스택뷰의 content-hugging과 compression-resistance를 설정하는 것은 영향을 주지 않습니다. 왜냐하면 스택뷰 자체는 내재된 컨텐트 크기를 갖지 않기 때문입니다.

만약 스택뷰의 fitting size를 스택뷰 바깥의 아이템에 상대적으로 대응하는 것을 적용하길 원한다면, 관계를 포착할 수 있는 명시적인 제약을 만들거나 스택 바깥의 아이템에 상대적으로 대응하는 스택의 컨텐트가 갖는 content-hugging, compression-resistance를 수정해야 합니다.

Interpreting Values

오토 레이아웃의 값은 항상 포인트 단위입니다. 그러나 이러한 측정의 정확한 단위는 연관이 있는 특성들과 뷰의 레이아웃 방향에 따라 달라질 수 있습니다.

Working with Constraints in Interface Builder

인터페이스 빌더에서 오토 레이아웃 제약들을 설정하는 주요 방법으로 세 가지가 있습니다. 뷰와 뷰 사이에서 컨트롤 드래그 할 수 있습니다. 핀 도구와 정렬 도구를 사용할 수도 있습니다. 인터페이스 빌더가 제약을 설정하도록 한 뒤에 결과를 편집하거나 수정할 수 있습니다. 각각의 접근법은 장단점이 있습니다. 대부분의 개발자는 선호하는 하나의 접근법을 찾습니다. 그러나 모든 접근법에 익숙해지는 것이 도구들 사이에서 빠르게 전환할 수 있도록 해줄 것입니다.

세 가지 방법과 관련해 씬 위에 객체 라이브러리로부터 뷰와 컨트롤을 드래그하는 것부터 시작하는 것이 좋습니다. 필요한 크기로 조절하고 위치를 수정할 수 있습니다. 캔버스 위에 뷰를 놓을 때, 인터페이스 빌더는 자동으로 왼쪽 위 코너를 기준으로 뷰의 현재 크기와 위치를 정의하는 포로토타입 제약들을 생성합니다.

앱은 프로토타입 제약들과 함께 빌드할 수 있고 실행할 수 있습니다. 이와 같은 제약들을 빠르게 시각화하고 UI를 테스트할 수 있도록 사용하는 것이 좋습니다. 그리고 명확하지 않은 제약들을 명확한 제약들로 대체하면 됩니다. 프로토타입 제약들로 앱을 개발하면 안 됩니다.

첫 번째 제약을 생성하면, 시스템은 제약에 의해 참조되는 뷰에서 모든 프로토타입 제약들을 제거합니다. 프로토타입 제약 없이 레이아웃은 더이상 모든 뷰에 대해 고유한 크기와 위치를 나타내는 충분한 제약을 갖지 않을 것입니다. 이는 모호한 레이아웃이 됨을 의미합니다. 영향을 받은 제약들은 갑자기 빨간색으로 나타날 것이고, Xcode는 몇 가지 경고를 생성할 것입니다.

당황하지 않고 레이아웃이 완성되기까지 제약들을 추가하면 됩니다. 하나의 제약을 추가하는 순간마다, 모호하지 않고 조건을 충족하는 레이아웃을 생성하기까지 필요한 제약들을 추가해야 합니다.

레이아웃 경고와 에러를 수정하기 위한 정보는 Debugging Auto Layout에 나와있습니다.

Debugging Auto Layout은 이 글의 다다음 글에 나옵니다.

Control-Dragging Constraints

두 뷰 사이에서 제약을 생성하려면 컨트롤 키를 누른 채로 하나의 뷰를 클릭해 드래그해서 다른 뷰에 드랍하면 됩니다.

마우스 클릭을 놓으면 인터페이스 빌더는 설정 가능한 제약의 목록을 HUD 메뉴에 보여줍니다.

인터페이스 빌더는 제약을 주려고 하는, 그리고 드래그 제스쳐의 방향에 따른 아이템에 기반해 제약의 집합을 선별해줍니다. 수평으로 드래그하면 뷰 사이에서 수평 간격을 설정할 수 있는 옵션을 얻을 수 있고, 뷰를 수직으로 정렬할 수 있는 옵션도 얻을 수 있습니다. 수직으로 드래그하면 수직 간격을 설정할 수 있는 옵션을 얻을 수 있고, 두 뷰를 수평으로 정렬할 수 있는 옵션도 얻을 수 있습니다. 두 가지 모두 다른 옵션들을 포함하고 있을 것입니다(뷰의 상대적 크기를 설정하는 것처럼)

NOTE
컨트롤 키를 누르고 드래그하는 제스쳐는 아이템을 캔버스에 있는 아이템에 그렇게 할 수도 있고, scene의 document outline에 있는 아이콘에 그렇게 할 수도 있습니다. 찾기 어려운 아이템에 제약을 둘 때 유용한 방법이 될 것입니다. 예를 들어 top 혹은 bottom 레이아웃 가이드와 같은 경우가 그렇습니다. document outline에 드래그하거나 document outline으로부터 드래그할 대 인터페이스 빌더는 제스쳐의 방향에 기반한 가능한 제약들의 목록을 걸러주지는 않습니다.

인터페이스 빌더는 뷰의 현재 프레임에 기반하는 제약들을 생성합니다. 그러므로 제약을 생성하기 전에 신중하게 뷰의 위치를 설정해야 합니다. 인터페이스 빌더의 가이드라인에 기반해 뷰를 정렬하면, 그에 적합한 제약을 설정해줘야 합니다. 필요하다면 나중에 제약을 편집할 수도 있습니다.

컨트롤 키를 누르고 드래그하는 것은 제약을 설정하기 위한 매우 빠른 방법을 제공합니다. 그러나 제약의 값들이 scene의 현재 레이아웃으로부터 추론되기 때문에 포인트로 마무리하는 것이 쉽습니다. 만약 정교한 컨트롤을 원한다면, 제약을 생성한 후 재검토하고 편집하거나 핀 도구와 정렬 도구를 사용하는 것이 좋습니다.

컨트롤 키를 누르고 드래그해서 제약을 생성하는 것과 관련해 더 많은 정보가 필요하다면, Auto Layout Help에 있는 Adding Layout Constraints를 살펴보시기 바랍니다.

Using the Stack, Align, Pin and Resolve Tools

인터페이스 빌더는 에디터 창의 오른쪽 아래 코너에 네 가지 오토 레이아웃 툴을 제공합니다. 그 네 가지는 스택, 정렬, 핀, Resolve Auto Layout Issues입니다.

제약을 생성하거나 한 번에 여러 가지 제약을 생성하길 원할 때 핀과 정렬 도구를 사용하는 것이 유용합니다. 이 도구를 사용하면 제약 조건을 만들기 전에 뷰를 정확한 위치에 놓을 필요가 없습니다. 대신 뷰들의 상대적 위치를 대략적으로 설정할 수 있고, 제약들을 추가할 수 있으며, 이후 프레임을 업데이트하면 됩니다. 이를 통해 오토 레이아웃이 정확한 위치를 계산할 것입니다.

Stack Tool

스택 도구는 스택뷰를 빠르게 생성하도록 돕습니다. 하나 혹은 하나 이상의 레이아웃에서 선택하고 스택 툴을 클릭하면 됩니다. 인터페이스 빌더는 스택뷰에서 선택된 아이템들을 끼워넣고, 컨텐츠에 기반해 적합한 크기로 스택의 크기를 다시 바꿉니다.

Note
시스템은 뷰들의 초기 기준 위치로부터 스택의 축과 정렬을 추론합니다. Attributes 인스펙터를 통해 축과 정렬(그리고 distribution과 간격 설정을)을 수정할 수 있습니다.

Align Tool

정렬 도구는 레이아웃에서 아이템들을 빠르게 정렬할 수 있도록 합니다. 정렬시키고 싶은 아잍템들을 선택하고 정렬 도구를 클릭하면 됩니다. 인터페이스 빌더는 몇 가지 가능한 정렬을 보여주는 작은 화면을 띄울 것입니다.

선택된 뷰를 정렬하기 위해 옵션을 선택하고, Add Constraints 버튼을 클릭하면 됩니다. 인터페이스 빌더는 정렬을 적용하기 위해 필요한 제약들을 생성합니다. 기본값으로 정렬들은 offset(edge들 혹은 중심들은 서로 정렬하는)을 갖지 않고 있고, 제약이 추가될 때 프레임이 업데이트되지 않습니다. 제약 생성 전에 앞서 언급한 변경점을 설정할 수 있습니다.

정렬 도구를 사용하기 전에 둘 혹은 그 이상의 뷰를 선택할 수 있습니다. 그러나 Horizontally in Container 혹은 Vertically in Container 제약들은 하나의 뷰에 추가될 수 있습니다. 한 번에 여러 제약들을 생성하기 위해 팝오버를 사용할 수 있습니다. 물론 한 번에 하나 혹은 둘 이상을 생성하는 것이 어색하긴 합니다.

더 많은 정보가 필요하다면 Auto Layout Help에 있는 Adding Auto Layout Constraints with the Pin and Align Tools를 살펴보시기 바랍니다.

Pin Tool

핀 도구는 근처에 있는 뷰를 기준으로 뷰의 위치를 빠르게 정의하도록 해줍니다. 혹은 크기를 정의할 수도 있습니다. 위치 혹은 크기를 고정시키고자 하는 아이템을 선택하고 핀 도구를 클릭하면 됩니다. 인터페이스 빌더는 몇 가지 옵셔들을 포함하는 작은 화면을 띄워줄 것입니다.

띄워진 작은 화면의 상단 부분은 선택한 아이템의 leading, top, trailing, bottom edge를 가까운 뷰에 대해 고정시킬 수 있도록 해줍니다. 보여지는 숫자는 캔버스에 있는 아이템들 사이의 현재 간격을 나타냅니다. 원하는 간격을 숫자로 입력할 수 있으며, 혹은 어떤 뷰가 어떤 뷰에 제약이 주어져야 하는지 설정하고 표준 간격을 선택하기 위한 disclosure 삼각형을 클릭할 수 있습니다. "Constrain to margins" 체크박스는 슈퍼뷰에 제약들을 슈퍼뷰의 마진에 사용할지 슈퍼뷰의 edge에 사용할지를 결정합니다.

띄워진 화면의 아래 부분은 아이템의 넓이 혹은 높이를 결정하도록 해줍니다. 널비와 높이 제약들은 현재 캔버스 크기에 기본값으로 되어 있으며, 다른 값들을 입력할 수도 있습니다. Aspect Ratio 제약은 아이템의 현재 aspect ratio 값을 사용합니다. 만약 이 비율을 변경하고자 한다면, 이 제약을 생성한 후 재검토하고 편집할 필요가 있습니다.

보통 고정을 위해 하나의 뷰를 선택합니다. 그러나 둘 혹은 둘 이상의 뷰를 선택할 수 있고, 선택한 뷰들의 넓이와 높이를 같도록 설정할 수 있습니다. 한 번에 여러 가지 제약들을 생성하는 것도 가능합니다. 혹은 추가한 제약을 따르는 프레임으로 업데이트할 수도 있습니다. 원하는 옵션을 설정한 이후 Add Constraints 버튼을 클릭해 제약들을 생성할 수 있습니다.

더 많은 정보가 필요하다면 Auto Layout Help에 있는 Adding Auto Layout Constraints with the Pin and Align Tools를 살펴보시기 바랍니다.

Resolve Auto Layout Issues Tool

Resolve Auto Layout Issues 도구는 일반적인 오토 레이아웃 이슈들을 수정하기 위한 몇 가지 옵션을 제공합니다. 메뉴의 상단에 있는 절반의 옵션들은 현재 선택된 뷰에만 영향을 미칩니다. 아래 절반의 옵션들은 씬에 존재하는 모든 뷰에 영향을 미칩니다.

이 도구를 사용해 현재 제약들에 기반한 뷰의 프레임을 업데이트할 수 있습니다. 혹은 캔버세 있는 뷰의 현재 위치에 기반해 제약들을 업데이트할 수 있습니다. 놓치고 있는 제약들을 추가할 수도 있고, 제약들을 제거할 수도 있으며, 혹은 인터페이스 빌더가 추천하는 제약들로 재설정할 수도 있습니다.

제약들을 추가하거나 재설정하는 명령은 The commands to add or reset constraints에 더 자세하게 나와 있습니다.

Letting Interface Builder Create Constraints

인터페이스 빌더는 몇 가지 혹은 모든 제약을 생성할 수 있습니다. 이 접근방식을 사용할 때 인터페이스 빌더는 캔버스에 있는 주어진 뷰의 현재 크기와 위치에 따라 최선의 제약을 추론하려 시도합니다. 뷰의 위치를 정확하게 놓아야 합니다. 간격에서 작은 차이가 상당히 다른 레이아웃의 결과를 초래할 수 있습니다.

인터페이스 빌더가 모든 제약을 생성할 수 있도록 하기 위해서 Resolve Auto Layout Issues 도구를 클릭하고 Reset to Suggested Constraints를 클릭하시기 바랍니다. 인터페이스 빌더는 선택된 뷰(혹은 씬에 있는 모든 뷰에 대해)에 필요한 모든 제약을 생성합니다.

다른 방법으로 직접 몇 가지 제약을 추가하고 Resolve Auto Layout Issues 도구에서 Add Missing Constraints를 클릭할 수 있습니다. 이 옵션은 모호하지 않은 레이아웃을 갖도록 필요한 제약을 추가합니다. 이 역시 선택된 뷰 혹은 씬에 있는 모든 뷰에 제약을 추가할 수 있습니다.

이 접근법은 모호하지 않고 조건을 충족하는 레이아웃을 빠르게 빌드해줍니다. 그러나 UI가 간단하지 않다면, 레이아웃의 결과가 원하는 것처럼 설정되어 있지 않을 수도 있습니다. 항상 의도한 결과를 얻을 수 있을 때까지 UI를 테스트하고 제약을 수정해야 합니다.

Finding and Editing Constraints

제약을 추가한 후 이를 찾을 수 있어야 하며, 볼 수도 있고 편집할 수도 있어야 합나디ㅏ. 제약에 접근하기 위한 몇 가지 옵션이 있습니다. 각 옵션은 제약을 조직화하고 표현하는 고유한 방법을 제공합니다.

Viewing Constraints in the Canvas

에디터는 캔버스에서 색깔이 있는 선으로 현재 선택된 뷰에 영향을 미치고 있는 모든 제약을 보여줍니다. 모양, 선이 그어진 유형, 선의 색깔은 제약의 현재 상태에 대해 많은 것을 말해줍니다.

  • I-bars (lines with T-shaped end-caps). I-bar는 여백의 크기를 보여줍니다. 이 여백은 두 아이템의 거리가 될 수도 있고 아이템의 높이와 넓이가 될 수도 있습니다.

  • Plain lines (straight lines with no end-caps). Plain line은 어느 곳에 edge가 정렬되어 있는지를 보여줍니다. 예를 들어 인터페이스 빌더는 둘 혹은 둘 이상의 leading edge를 정렬시킬 때 간단한 선을 사용합니다. 이 선들은 아이템들 사이의 0포인트 간격, 아이템들을 연결시켜주기 위해 사용됩니다.

  • Solid Lines. Solid line은 요구되는 제약들(우선순위 = 1000)을 나타냅니다.

  • Dashed Lines. Dashed line은 선택적 제약들(우선순위 < 1000)을 나타냅니다.

  • Red Lines. 제약에 의해 영향을 받는 하나의 아이템은 에러를 갖고 있습니다. 이 경우 모호한 레이아웃이거나 조건을 충족시키지 못하는 레이아웃입니다. 더 많은 정보가 필요하다면 인터페이스 빌더의 아웃라인 뷰에 있는 issues navigator 혹은 disclosure arrow를 살펴보시기 바랍니다.

  • Orange Lines. Orange line은 이 제약에 영향을 받는 아이템 중 하나의 프레임이 현재 제약에 기반할 때 정확한 위치가 아니라는 것을 나타냅니다. 인터페이스 빌더는 점선인 outline으로 프레임에 대한 계산된 위치를 보여주기도 합니다. Resolve Auto Layout Issues tool에 있는 Update Frames 명령을 통해 계산된 위치로 아이템을 이동시킬 수 있습니다.

  • Blue Lines. 제약에 영향을 받는 아이템들은 모호하지 않고 조건을 충족하는 레이아웃이며, Auto Layout 엔진이 계산한 정확한 위치에 아이템의 프레임이 놓여있음을 의미합니다.

  • Equal Badges. 인터페이스 빌더는 각각의 아이템에 대한 분리된 바를 통해 두 아이템이 같은 넓이 혹은 같은 높이가 되도록 하는 제약을 보여줍니다. 두 바는 '=' 사인을 포함하고 있는 파란색 badge를 갖고 있습니다.

  • Greater-than-or-equal and less-than-or-equal badges. 인터페이스 빌더는 '>=' 사인 혹은 '<=' 사인을 갖는 작은 파란색 badge를 통해 greater-than-or-equal-to와 less-than-or-equal-to 관계를 보여줍니다.

Listing Constraints in the Document Outline

인터페이스 빌더는 document outline에 모든 제약을 리스트로 보여줍니다. 이는 뷰를 포함하고 있는 상위 속에서 그룹화되어 보여집니다. 제약들은 제약의 두 아이템을 모두 포함하는 가장 가까운 뷰가 갖습니다. 이와 같은 계산으로 각 뷰는 스스로와 모든 하위 뷰를 갖고 있고, 씬의 루트뷰에 의해 포함되는 top, bottom 레이아웃 가이드를 포함합니다.

제약들은 아웃라인에 펼쳐질 수 있지만, 대부분의 제약은 씬의 루트뷰 아래에 있습니다. 모든 제약들을 확인하려면 전체 뷰 계층구조를 확장하시기 바랍니다.

제약들은 pseudocode를 사용해 목록으로 보여집니다. 이 목록은 보통 길고 유사한 뷰의 집합으로 시작하며, 그렇기 때문에 의미있는 정보를 보려면 아웃라인을 넓게 해야 할 것입니다. 아웃라인에서 제약을 선택하면 캔버스의 제약이 강조되어 보여집니다. 파악하고자 하는 제약을 빠르게 확인하기 하려면 아웃라인에서 제약을 선택하면 됩니다.

간단한 씬에서 아웃라인은 모든 제약을 한 번에 볼 수 있는 곳입니다. 그러나 레이아웃이 더 복잡해지면, 특정 제약들을 확인하기 어려워집니다. 그렇기 때문에 한 번에 하나의 뷰에 대한 제약을 확인하는 것이 좋습니다. 이는 캔버스에서 뷰를 선택하거나 사이즈 인스펙터에서 확인이 가능합니다.

Finding Constraints in the Size Inspector

사이즈 인스펙터는 현재 선택된 뷰에 영향을 주고 있는 모든 제약을 목록으로 보여줍니다. 제약들은 하나의 아웃라인으로 나타나고, 선택적 제약들은 점선 아웃라인으로 나타납니다. 설명은 제약에 대한 중요한 정보를 목록으로 보여줍니다. 이는 항상 영향을 받는 특성을 포함하고 제약이 걸려 있는 다른 아이템을 포함합니다. 또한, 관계, 상수값, 멀티플라이어 혹은 비율을 포함하고 있습니다.

위에 있는 스크린샷의 상단 다이어그램은 어떤 특성이 제약들에 의해 영향을 받는지를 보여주고 있습니다. 다이어그램의 특성들 중 하나 혹은 하나 이상을 선택해 제약들의 리스트 중 하나를 살펴볼 수 있습니다. 그렇게 하면 선택된 특성들에 의해 영향을 받는 제약들만을 볼 수 있습니다.

이에 대한 더 많은 정보는 Auto Layout Help에 있는 Viewing the Complete List of Layout Constraints for an Item을 살펴보시기 바랍니다.

Examining and Editing Constraints

캔버스 혹은 document outline에 있는 제약을 선택할 때, 특성 인스펙터는 제약의 모든 특성들을 보여줍니다. 이는 제약 등식으로부터 모든 값을 포함하고 있습니다. 첫 번째 아이템, 관계, 두 번째 아이템, 상수, 멀티플라이어가 있습니다. 특성 인스펙터는 제약의 우선순위와 identifier를 보여주기도 합니다.

NOTE
제약의 identifier 속성은 설정한 이름을 제공하고, 그렇게 함으로써 콘솔 로그와 다른 디버깅 task에서 쉽게 해당 제약을 찾을 수 있도록 합니다.

플레이스홀더로 제약에 마크를 남길 수도 있습니다. 이와 같은 제약들은 설계 타임에만 존재합니다. 앱이 실행될 때 이들은 레이아웃에 포함되어있지 않습니다. 런타임에 제약들을 동적으로 추가하고자 할 때 플레이스홀더 제약들을 추가합니다. 모호하지 않고 조건을 충족하는 제약을 일시적으로 추가하는 것을 통해 인터페이스 빌더에 있는 경고와 에러를 지울 수 있습니다.

상수, 우선순위, 멀티플라이어, 관계, identifier, 플레이스홀더 특성을 자유롭게 수정할 수 있습니다. 그러나 첫 번째와 두 번째 아이템은 옵션에 제약이 있습니다. 첫 번째와 두 번째 아이템(멀티플라이어와 상수를 역전시키면서)을 바꿀 수 있습니다. 그리고 아이템의 특성을 바꿀 수도 있습니다. 하지만 아이템 자체를 바꿀 수는 없습니다. 만약 제약을 완전히 다른 아이템으로 옮기고 싶다면 제약을 삭제하고 새로운 제약으로 대체해야 합니다.

몇 가지 편집은 사이즈 인스펙터에서 직접적으로 가능합니다. edit 버튼을 클릭해 제약의 관계, 상수, 우선순위, 멀티플라이어를 바꿀 수 있도록 해주는 작은 화면을 띄울 수 있습니다. 추가적인 변경점을 만드려면 선택을 위해 제약을 더블 클릭하고 특성 인스펙터에서 열 수 있습니다.

더 많은 정보는 Auto Layout Help에 있는 Editing Auto Layout Constraints를 살펴보시기 바랍니다.

Setting Content-Hugging and Compression-Resistance Priorities

content-hugging과 compression-resistance를 설정하려면, 캔버스 혹은 document outline에 있는 뷰를 선택해야 합니다. 사이즈 인스펙터를 열고 Content Hugging Priority와 Compression Resistance Priority 세팅을 찾을 때까지 스크롤을 내리면 됩니다.

또한, 인터페이스 빌더에서 뷰의 내적 크기를 설정할 수 있습니다. 기본값으로 인터페이스 빌더는 뷰의 intrinsicContentSize 메소드로부터 반환되는 크기를 사용합니다. 그러나 디자인 타임에 다른 크기가 필요하다면, 플레이스홀더 내적 컨텐트 크기를 설정할 수 있습니다. 이 플레이스홀더는 오직 인터페이스 빌더에서만 뷰의 크기에 영향을 미칩니다. 런타임에서 뷰에 영향을 미치지는 않습니다.

더 많은 정보가 필요하다면 Auto Layout Help에 있는 Placeholder Intrinsic Size for a Custom View

iOS-Only Features

iOS는 Auto Layout과 상호작용하는 몇 가지 고유한 기능을 추가합니다. 이 기능들은 top, bottom 레이아웃 가이드, 뷰의 레이아웃 마진, 뷰의 readable content guide, 뷰의 semantic content를 포함합니다.

Top and Bottom Layout Guides

top과 bottom 레이아웃 가이드는 현재 활성화된 뷰 컨트롤러에 대한 시각화할 수 있는 컨텐트 영역의 위, 아래 edge를 표현합니다. UIKit에 있는 투명한 바(예를 들어 status bar, navigation bar, tab bar) 아래로 컨텐트가 확장되지 않기를 원한다면, 각각의 레이아웃 가이드에 컨텐트를 고정시킬 수 있도록 Auto Layout을 사용해야 합니다.

레이아웃 가이드는 UILayoutSupport 프로토콜을 채택함으로써 가이드와 뷰가 갖는 각각의 edge 사이에 거리르 측정하는 길이 속성 가이드를 제공받을 수 있습니다. 구체적으로 아래와 같습니다.

  • top 레이아웃 가이드에서 길이는 포인트를 통해 뷰 컨트롤러의 뷰 top과 뷰를 오버레이하는 가장 하위 바의 bottom 사이 거리를 나타냅니다.

  • bottom 레이아웃 가이드에서 길이는 포인트를 통해 뷰 컨트롤러의 bottom과 뷰를 오버레이하는 바(예를 들어 tab bar)의 top 사이 거리를 나타냅니다.

이 가이드들은 top, bottom, 높이 특성을 지원하는 동시에 제약에 있는 아이템들 역할을 합니다. 보통 top 레이아웃 가이드의 bottom 특성 혹은 bottom 레이아웃 가이드의 top 특성에 제약을 둡니다. 가이드들은 topAnchor, bottomAhchor, heightAnchor 속성들을 제공하기도 합니다. 이를 통해 제약들의 코드 생성을 간단하게 만들 수 있습니다.

적합한 방법으로 루트 뷰의 top 혹은 bottom edge에 제약을 생성할 때, 인터페이스 빌더는 옵션으로서 자동으로 top과 bottom 레이아웃 가이드를 제공합니다. 레이아웃 가이드가 뷰의 가장 가까운 이웃이라면, 인터페이스 빌더는 기본값으로 가이드를 사용합니다. pin 도구를 사용할 때, 필요하다면 레이아웃 가이드와 루트 뷰의 edge를 서로 바꿀 수 있습니다. disclosure triangle을 클릭하면 됩니다.

Layout Margins

오토 레이아웃은 각 뷰의 마진을 정의합니다. 이 마진들은 뷰의 edge와 하위 뷰 사이의 여백을 설명합니다. layoutMargins 혹은 layoutMarginsGuide 속성을 사용해 뷰의 마진에 접근할 수 있습니다. layoutMargins 속성은 UIEdgeInsets 구조체로 마진을 얻거나 설정할 수 있도록 해줍니다. layoutMarginsGuide 속성은 UILayoutGuide 객체로 마진에 대한 읽기 전용 접근을 제공합니다. 추가적으로 슈퍼뷰의 마진과 상호작용하는 뷰의 마진을 어떻게 결정할지를 위한 preservesSuperviewLayoutMargins 속성을 사용할 수 있습니다.

각 측면의 마진의 기본값은 8포인트입니다. 필요에 따하 이 마진을 수정할 수 있습니다.

NOTE
시스템은 View Controller의 루트뷰가 갖는 마진들을 설정하고 관리합니다. top과 bottom 마진은 0포인트로 설정됩니다. 이는 바 아래에 컨텐트를 쉽게 확장해주도록 합니다. 사이드 마진은 컨트롤러가 어떻게 그리고 어디에 나타나는지에 따라 다릅니다. 16 혹은 20 포인트가 될 수 있습니다. 이 마진을 바꿀 수는 없습니다.

뷰를 슈퍼뷰에 제약을 둘 때, 보통 뷰의 edge 대신 레이아웃의 마진을 사용합니다. UIKit에 NSLayoutAttribute 열거형은 top, bottom, leading, trailing, left, right 마진을 나타내기 위한 몇 가지를 정의합니다. 마진을 기준으로 center X와 center Y를 포함하기도 합니다.

인터페이스 빌더에서 컨트롤 키를 누르고 드래그를 통해 뷰와 슈퍼뷰 사이의 제약은 기본값인 마진 특성들을 사용합니다. 핀 도구를 사용할 때, "Constrain to margins" 체크박스를 토글할 수 있습니다. 체크가 되어 있지 않다면, 슈퍼뷰의 edge를 사용합니다. 유사하게 특성 인스펙터에서 제약을 편집할 때, First Item과 Second Item의 pop-down 메뉴는 "Reliatve to margin" 옵션을 포함합니다. 마진 특성을 사용하기 위해 이 아이템을 선택할 수 있습니다. edge를 사용하려면 선택하지 않으면 됩니다.

마지막으로 슈퍼뷰의 제약을 생성할 때 코드로 작성한다면, layoutMarginsGuide 속성을 사용하고 레이아웃 가이드에 대한 제약들을 직접 생성할 수 있습니다. 이는 제약을 생성하기 위한 가이드의 레이아웃 anchor를 사용할 수 있도록 해주고, 읽기 쉬운 간소화된 API를 제공합니다.

Readable Content Guides

뷰의 readableContentGuide 속성은 뷰 내부에 있는 텍스트 객체들에 대한 최대 최적 넓이를 정의하는 레이아웃 가이드를 포함합니다. 컨텐트는 헤드를 이동시키는 것 없이 사용자가 읽을 수 있도록 충분히 좁습니다.

이 가이드는 항상 뷰의 마진 내부에서 항상 중심에 위치하고, 절대로 마진을 넘어서 확장하지 않습니다. 가이드의 크기는 시스템의 동적 타입 크기에 의존해 다양합니다. 시스템은 사용자가 더 큰 폰트를 선택할 때 더 넓은 가이드를 생성합니다. 사용자는 보통 읽는 동안 기기를 더 멀리 잡기 때문입니다.

인터페이스 빌더에서 뷰의 마진을 레아이웃 마진으로 나타낼지 혹은 readable content guide로 나타낼지를 설정할 수 있습니다. 뷰(보통 뷰 컨트롤러의 루트 뷰)를 선택하고 사이즈 인스펙터를 열어둡니다. 만약 Follow Readable Width 체크박스를 선택하면, 뷰의 마진에 그려진 모든 제약들을 readable content guide로 사용하도록 합니다.

NOTE
대부분의 기기에서 readable content guides와 the layout margins는 작은 차이만 있거나 차이가 없습니다. 아이패드의 landscape 오리엔테이션에서 작동하는 경우에만 차이가 명확해집니다.

Semantic Content

만약 leading과 trailing 제약을 사용해 뷰의 레이아웃을 설정하면, 뷰는 left-to-right 언어(영어와 같은)와 right-to-left 언어(아랍어와 같은) 사이를 자동으로 뒤집습니다. 그러나 몇 가지 인터페이스 요소들은 읽는 방향에 기반해 그들의 위치를 바꾸지 않습니다. 예를 들어 물리적 방향(위, 아래, 왼쪽, 오른쪽)에 기반한 버튼은 항상 같은 기준의 오리엔테이션에서 머무릅니다.

뷰의 semanticContentAttribute 속성은 left-to-right, right-to-left 언어 사이에서 전환이 일어날 때 뷰의 컨텐트가 뒤집어져야 하는지를 결정합니다.

인터페이스 빌더에서특성 인스펙터에 있는 Semantic option을 설정하면 됩니다. 만약 값이 Unspecified라면, 뷰의 컨텐트는 읽는 방향에 따라 뒤집어집니다. 만약 Spatial, Playback 록은 Force Left-to-Right으로 설정되어 있다면, 컨텐트는 항상 leading edge를 왼쪽에, trailing edge를 오른쪽에 놓여집니다. Force Right-to-Left는 항상 leading edge를 오른쪽에, trailing edge를 왼쪽에 있는 상태로 컨텐트를 둡니다.

Rules of Thumb

아래 가이드라인이 오토 레이아웃을 성공적으로 완성할 수 있도록 도와줄 것입니다. 몇 가지 예외도 있습니다. 그러나 바꾸기를 결심한다면, 진행하기 전에 멈춰두고 접근법을 조심스럽게 생각해봐야 합니다.

  • 뷰의 기하를 frame, bounds, center 속성을 사용해 구체화하면 안 됩니다.

  • 가능하면 스택뷰를 사용하기 바랍니다.

  • 스택뷰는 갖고 있는 컨텐트의 레이아웃을 관리합니다. 이는 필요한 나머지 레이아웃에 대한 제약 로직을 간단하게 해줍니다. 스택뷰가 원하는 제약을 제공하지 못할 때에만 커스터마이징으로 제약들에 의존하도록 해야 합니다.

  • 뷰와 그 뷰에 가장 가까운 이웃에 제약을 생성해야 합니다.

  • 만약 두 개의 버튼이 있고 서로 옆에 있다면, 첫 번째 버튼의 trailing edge에 두 번째 버튼의 leading edge의 제약을 둬야 합니다. 두 번째 버튼은 일반적으로 첫 번째 버튼을 가로질러 뷰의 edge에 도달하는 제약을 가지면 안 됩니다.

  • 뷰에 고정된 높이와 넓이를 설정하는 것은 피하는 것이 좋습니다.
    오토 레이아웃의 전체 포인트는 변화에 동적으로 반응합니다. 크기를 고정으로 설정하는 것은 뷰가 적응할 수 있는 능력을 제거합니다. 그러나 아마도 뷰의 최소 혹은 최대 크기를 설정하길 원할 수도 있을 것입니다.

  • 제약을 설정하는 데 문제가 발생한다면, 핀과 정렬 도구를 사용해보시기 바랍니다. 이 도구들은 컨트롤 키를 누르고 드래그하는 것보다 다소 느리지만, 제약을 생성하기 전에 연관이 있는 정확한 값과 아이템을 검증할 수 있도록 해줍니다. 이런 추가적인 검사는 처음 시작할 때 유용할 수 있습니다.

  • 아이템의 프레임을 자동으로 업데이트할 때는 신중해야 합니다. 만약 아이템이 크기와 위치를 완전히 구체화할 수 있는 충분한 제약을 갖지 못하는 경우, 업데이트의 움직임이 정의되지 않습니다. 뷰는 보통 높이 혹은 넓이가 0으로 설정될 때나 우연히 스크린 밖에 위치했을 때 사라집니다.
    언제라도 아이템의 프레임을 업데이트할 수 있고, 필요한 경우 변화를 되돌릴 수 있습니다.

  • 레이아웃에 대해 모든 뷰를 파악하기 쉬운 이름으로 설정해주는 것이 좋습니다. 이를 통해 도구를 사용할 때 더 쉽게 뷰를 확인할 수 있습니다.
    시스템은 뷰의 텍스트 혹은 타이틀에 기반해 레이블과 버튼의 이름을 자동으로 지정합니다. 다른 뷰는 아이덴티티 인스펙터에서 Xcode 구체화 레이블을 설정할 필요가 있습니다(혹은 더블 클릭하고 뷰의 이름은 document outline에서 편집할 수 있습니다).

  • right와 left 대신 항상 leading과 trailing 제약을 사용해야 합니다.
    어떻게 뷰가 leading과 trailing edge를 해석할지를 적용할 수 있으며, semanticContentAttribute 속성 (iOS) 혹은 userInterfaceLayoutDirection property 속성 (OS X)을 사용하는 것을 통해 그렇게 할 수 있습니다.

  • iOS에서 뷰 컨트롤러의 루트 뷰의 edge에 아이템이 제약을 둘 때, 아래와 같은 제약을 사용하시기 바랍니다.

    • Horizontal constraints. 대부분의 컨트롤에서 레이아웃 마진 제약은 0포인트를 사용하시기 바랍니다. 시스템은 어떤 기기를 기반으로 하는지와 어떻게 앱이 뷰 컨트롤러를 나타내고있는지에 기반해 정확한 여백을 자동으로 제공합니다.
      루트 뷰의 마진과 마진을 채우는 텍스트 객체에서 레이아웃 마진 대신 readable content guides를 사용해야 합니다.
      루트 뷰의 edge와 edge(예를 들어 배경 이미지)를 채워야 하는 아이템에서 뷰의 leading과 trailing edge를 사용해야 합니다.
    • Vertical constraints. 만약 뷰가 바 밑으로 확장한다면, top과 bottom 마진을 사용해야 합니다. 이는 특히 스크롤뷰에서 흔하게 발생하며, 바 밑으로 컨텐트가 스크롤될 수 잇도록 해줍니다. 그러나 스크롤뷰의 contentInsetscrollIndicatorInsets 속성을 수정해야 함을 알아야 합니다. 이는 컨텐트의 초기 위치를 정확하게 설정하기 위함입니다.
      만약 뷰가 바 밑으로 확장되지 않는다면, 뷰의 제약은 top과 bottom 레이아웃 가이드에 있어야 합니다.
  • 뷰를 코드로 초기화할 때, translatesAutoresizingMaskIntoConstraints 속성을 NO로 설정해야 합니다. 기본값으로 시스템은 뷰의 프레임과 오토리사이징 마스크에 기반해 제약들의 집합을 자동으로 생성합니다. 고유한 제약을 추가할 때, 이들은 autogenerated 제약과 피할 수 없는 충돌이 발생시킵니다. 이는 조건을 충족하지 못하는 레이아웃입니다.

  • OS X와 iOS는 레이아웃을 다른 방식으로 계산한다는 것을 인식해야 합니다.
    iOS에서 오토 레이아웃은 윈도우의 컨텐트와 윈도우 크기 모두를 수정할 수 있습니다.
    iOS에서 시스템은 씬의 크기와 레이아웃을 제공합니다. 오토 레이아웃은 오직 씬의 컨텐트만 수정할 수 있습니다.
    이와 같은 차이는 상대적으로 중요해보이지 않을 수 있지만, 레이아웃을 어떻게 디자인하느냐와 어떻게 속성들을 사용하는지에 중요한 영향을 미칠 수 있습니다.

0개의 댓글