오토 레이아웃, 정확히 알고 쓰자!

오토 레이아웃 파헤치기

뷰 계층구조에서 모든 뷰는 당연하게도 위치와 크기를 가지고 있다. iOS에서 뷰에 위치와 크기를 부여할 수 있는 세 가지 방법이 있다. 전통적인 방식은 뷰의 프레임을 설정하는 것이며, 이 외에 뷰의 autoresizing mask를 설정하기, 그리고 오토 레이아웃을 이용하여 뷰 사이즈와 위치에 제약조건을 설정하는 방식이 있다. 이 중 오토 레이아웃을 살펴보겠다.

0. 뷰를 배치하는 방법

오토 레이아웃을 설명하기 전에 프레임 기반 레이아웃과 오토 리사이징 마스크에 대해서 짚고 넘어가려고 한다. 프레임은 뷰에 origin(x, y)과 size(height, width)를 직접 설정함으로써 뷰를 배치한다. 대놓고 상수 값을 설정하기 때문에 직관적이고 예측하기 쉽다. 하지만 복잡한 뷰의 경우 일일이 모든 뷰의 frame 값을 계산해야한다. 더 심각한 문제는 유연성인데, 만약 가로 모드에서 세로 모드로 변경했다면? 디바이스가 아이폰 7 에서 아이폰 7 플러스로 변경됐다면? 모든 뷰의 프레임 4개 값을 전부 바꿔줘야한다.

이렇게 레이아웃에 변화를 주는 것에는 외부 변화와 내부 변화가 있다. 외부변화는 디바이스의 화면 모드 전환, 크기 변화와 같이 슈퍼 뷰의 크기나 모양이 변경할 때 발생한다. 내부변화는 뷰나 컨트롤의 사이즈가 변경될 때 발생한다. 컨텐트, 로컬라이제이션, 다이나믹 타입 지원 등이 이에 속한다.

오토 리사이징 마스크는 슈퍼 뷰가 변할 때 어떻게 변할지를 정의한다. 예를 들어 width 비율이 슈퍼 뷰의 2분의 1이 되도록 하고싶다면, 슈퍼 뷰의 2분의 1 크기에서 오토 리사이징 설정을 해주면 된다. 하지만 오토 리사이징은 외부 변화에만 반응한다.

오토 레이아웃은 외부 변화와 내부변화 모두에 반응하는 동적인 레이아웃 설정 방식이다.

이러한 단점들을 포용하는 것이 바로 오토 레이아웃이다.

1. 오토 레이아웃이 뭔가요?

뷰에게 제약조건을 걸고, 이를 기반으로 뷰 계층구조에 있는 모든 뷰의 크기와 위치를 동적으로 계산하는 것이 오토 레이아웃이다.

1-1. 제약 조건

제약조건이 무엇인지 알아보기 위해 아래 식을 예로 들어보자.

// 1
RedView.leading = 1.0 x BlueView.trailing + 8.0
// 2
View.height = 2.0 * View.width + 0.0
// 3
View.height = 40.0

# item과 attribute

먼저 뷰나 레이아웃 가이드를 item이라 하고 leading, trailing, height, width 등은 attribute라고 한다. 제약조건은 두 개의 아이템 사이의 관계를 정의 할 수도 있고(1), 하나의 아이템의 두 개의 다른 attribute 사이의 관계를 정의할 수도 있으며(2), 아이템의 height이나 width에 상수 값을 설정(3)해 줄 수도 있다.

오토 레이아웃이 올바르게 작동하려면 unambiguous, satisfiable 레이아웃을 만족해야한다. 즉, 제약조건이 모호하지 않고(nonambiguous), 주어진 제약조건을 만족해야(Satisfiable) 한다. 하지만 모든 제약조건을 만족 해야하는 것은 아니다. 제약조건에 우선순위가 존재한다.

# 제약조건 우선순위

우선순위는 1-1000까지 있으며 디폴트 값은 1000으로 무조건 지켜야하는 제약조건이다. 1000 아래는 선택적 제약조건이다. 런타임 시점에 오토 레이아웃 엔진은 제약조건을 검토한다. 우선순위가 1000이면 무조건 만족해야하고 그 아래면 제약조건이 모호하거나 제약조건끼리 충돌이 나도 경고가 생기지 않는다(무시하고 다음 제약조건을 체크하면 되니까). 우선순위가 큰 제약조건이 먼저 충족된다.

# intrinsic content size

어떤 뷰들은 특별히 제약조건을 주지 않아도 사이즈가 설정되기도 한다. 뷰에 라벨을 추가하고 라벨의 컨텐트 내용이나 폰트를 변경했을 때, 아무것도 설정해 주지 않아도 알아서 라벨 사이즈가 조정되는 것을 경험했을 것이다. 몇몇 뷰들이 컨텐트 사이즈에 맞는 고유 사이즈를 가지고 있기 때문인데, 이를 intrinsic content size라고 한다. 이와 관련하여 두 가지 속성이 또 있다.

Content hugging priority: intrinsic content size보다 커지기를 거부한다. 우선순위가 클수록 커지는 것을 저항한다. 작을수록 커진다.

Content compression resistance priority: intrinsic content size보다 작아지기를 거부한다.

2. 오토 레이아웃 코드로 표현하기

업데이트 패스는 제약조건을 업데이트 한다. 시스템은 뷰 계층구조를 순회하며 모든 뷰 컨트롤러에서 updateViewContraints 메소드를, 모든 뷰에서 updateConstraints를 호출한다.

레이아웃 패스는 뷰의 프레임을 재배치한다. 시스템은 뷰 계층구조를 다시 순회하고 뷰 컬트롤러에서 viewWillLayoutSubviews를, 모든 뷰에서 layoutSubviews를 호출한다.


참고

Auto Layout Guide