오토레이아웃(Auto layout)은 iOS 앱을 처음 개발하는 초보에게
가장 힘들었던 것 중 하나다.
새로운 걸 배웠으니 앱을 실제로 만들어보자! 라고 패기있게 시작하지만,
항상 화면이 내 마음처럼 그려지지 않아서 고통을 받았다.
옛날 옛적 PPT 그리던 짬이 있는데,
스토리보드에 레이아웃 잡는 것 쯤이야!!
...는 무슨 막상 실제 앱 로직은 하나도 짜지 못하고
몇 시간이 지나버린 적도 많았다.
'이게 그렇게 어려워야할 일이 아닌 거 같다'는 느낌이 들어 더 화가 났다.
뷰 그리는 건 적당히 하고 빨리 컨트롤러나 모델 로직을 짜고 싶은데...
하지만 내 화면은 말을 듣지 않았다.
마치 한쪽을 집어넣으면 반대쪽이 툭 튀어나오는 여행 가방처럼,
수정할 때마다 계속 스토리보드에 빨간 오류를 띄웠다.
저번 옵셔널 글에서도 말했지만,
자꾸 오류를 띄우는 녀석은 뭔가 밉상(?)이 된다.
답답한 마음에 오토레이아웃의 개념 원리를 제대로 파보기로 했다.
오토레이아웃을 설명한 애플의 공식 문서를 참고했다.
iOS의 UI는 뷰(View) 안에
여러 하위 뷰(Subview)가 들어간 트리 구조로 되어있다.
iOS가 알맞게 UI의 레이아웃을 잡으려면,
개발자가 모든 뷰의 '위치'와 '크기'를 지정해줘야 한다.
이 위치, 크기를 하나하나 절대값으로 줄 수도 있다.
실제로 예전에는 그렇게 했었고.
하지만 다양한 크기의 화면에도 문제 없는 화면을 만들려면,
위치와 크기를 '상대적으로' 지정해줄 필요가 있다.
우리가 30명의 학생들을 교실에 앉혀야 한다고 생각해보자.
절대적인 위치
선생님: 지수는 3분단 2번째 자리 앉고.
수철이는 1분단 5번째 자리 앉고.
형식이는 2분단 4번째 자리 앉고.
선생님: 다 자리 잡았지? 수업 시작한다.
상대적인 위치
수철: 선생님 저는 지수 옆에 앉아야 돼요.
지수: 선생님 전 에어컨 옆자리 앉을 거에요.
형식: 선생님 전 칠판에서 2줄이상 떨어지기 싫은데요.
진영: 선생님 지각해도 티 안 나게 맨 구석자리로 주세요.
선생님: 하... 머리 아프네. 😵💫
이걸 어떻게 다 만족시키면서 자리를 정하지?
이 머리아픈 일을 대신 해주는 것이 바로 오토레이아웃이다.
이 오토레이아웃의 목적은 아주 단순하다.
모든 뷰에 대해 다음 4개의 값을 알아내는 것이다.
어떤 뷰를 화면에서 보여주기 위해서,
X축/Y축의 크기/위치를 알아야 한다.
오토레이아웃이라는 게 복잡해보이지만,
결국 이 4개 값을 구하는 시스템이라는 걸
알면 한결 이해가 쉬워진다.
개발자가 준 정보를 가지고
모든 뷰의 이 4가지 값을 알아냈을 때,
오토레이아웃 엔진은 화면을 문제 없이 그릴 수 있게 된다.
그러면 개발자는 어떻게 정보를 줄까?
개발자는 '조건(Constraints)'를 준다.
y = ax+b
이다.오토레이아웃의 핵심은 조건(Constraints)이다.
개발자는 절대값으로 X/Y축의 위치/크기를 결정해주는 대신, 조건을 설정해서
오토레이아웃 엔진이 알아서 값을 계산하게 한다.
조건은 상대적인 관계를 나타낸다.
예를 들면 이런 식이다.
이걸 수식으로 나타내면,
우리가 중학교 수학시간에 많이 배웠던 '방정식'이 된다.
y = ax + b
x, y는 서로 다른 뷰의 '속성'을 나타낸다.
(속성에 대해서는 아래에서 설명한다.)
a, b는 개발자가 지정하는 임의의 수다.
만약 a와 b가 정해진 이런 방정식이 여러개 주어지면,
컴퓨터는 그걸 가지고 x, y의 값을 구해낼 수 있다.
다음 두 방정식에서 x, y의 값은 얼마일까?
y = x + 30
y = 2x
맞다. 중학교 때 배운 연립 방정식이다.
y = 2x를 다른 식에 대입해 풀어보자.
y는 20, x는 10이다.
이 방정식을 컴퓨터는 엄청나게 빨리 풀 수 있다.
오토레이아웃 엔진은 주어진 방정식(조건)을 가지고 x, y 값을 구한다.
그 x,y 값을 바탕으로 뷰의 위치/크기를 결정한다.
위치/크기를 가지고 화면에 렌더링한다.
덕분에 최상위 뷰 (화면)의 크기나 비율,
다른 뷰의 변화에 반응해서 레이아웃이 유연하게 변할 수 있다.
다시 말해,
iOS가 모든 뷰의 4가지 값(= X축/Y축의 위치/크기)를 구할 수 있도록,
개발자는 뷰와 뷰 사이의 상대적인 관계를 표현한 방정식 (=조건)을 여러개 만들어서 전달한다.
이게 오토레이아웃의 핵심이다.
조건(Constraints)는 뷰의 '속성(attributes)' 간 관계를 설정한다.
뷰의 속성은 다음과 같다.
(출처: Apple Developer)
뷰 직사각형의 상하좌우 테두리를 뜻한다. 직관적이어서 별로 설명할 게 없다.
다만, Leading은 'Text가 시작하는 부분'이라는 뜻이다.
보통은 왼쪽을 뜻하는 경우가 많다.
하지만 오른쪽에서부터 왼쪽으로 시작하는 언어도 있으므로,
그런 언어를 나타내는 뷰에서는 오른쪽이 Leading이다.
가로, 세로 중심축을 뜻한다.
Top과 Bottom 사이의 높이, Leading과 Trailing 사이의 너비를 말한다.
크기 속성에 있는 Height와 Width는 상수로 정해줄 수 있다.
(좀 더 정확하게 말하면, NotAnAttribute
에 대한 관계로 나타낼 수 있다.)
A의 Height = (0 *
NotAnAttribute
) + 40 ✅
B의 Width = (0 *NotAnAttribute
) + 450 ✅
하지만 위치 속성은 상수로 설정할 수 없고,
항상 다른 뷰와의 관계로 표시해야 한다.
A의 Top = (0 *
NotAnAttribute
) + 200 ❌
A의 Top = (1.0 * B의 Top) + 20 ✅
크기 속성과 위치 속성은 같은 조건에 사용할 수 없다.
A의 Top = B의 Height + 100 ❌
B의 Center X = A의 Width - 200 ❌
다시 말해, '조건'은 하나의 뷰와 또다른 뷰의 속성 간 관계를 지정한다.
흔한 조건의 예시를 보자.
// 높이를 40포인트로 지정한다.
View.height = 0.0 * NotAnAttribute + 40.0
// 두 버튼 사이의 간격을 8포인트 띄운다.
Button2.leading = 1.0 * Button1.trailing + 8.0
// 두 버튼의 왼쪽을 정렬한다.
Button1.leading = 1.0 * Button2.leading + 0.0
// 두 버튼의 너비를 똑같이 만든다.
Button1.width = 1.0 * Button2.width + 0.0
// 상위 뷰와 가운데 정렬을 한다.
View.centerX = 1.0 * Superview.centerX + 0.0
// 높이를 너비의 두배로 만든다.
View.height = 2.0 * View.width + 0.0
여기서 살짝 헷갈리는 부분이 있다.
'속성'을 정해줬다고 해서 오토레이아웃 엔진이 원하는 '4가지 값'이 항상 충족되는 건 아니다.
주어진 속성을 가지고 크기와 위치를 모두 알아낼 수 있어야 한다.
아래 그림을 보자.
위와 같이 상위 뷰의 위치와 크기가 잡혀 있는 상태에서
하위 뷰 테두리 4개(Top / Bottom / Leading / Trailing)의 조건을 모두 정해준다.
이러면 Auto layout이 문제 없이 위치를 계산해낼 수 있다. ✅
이번에는 X 중심축, Y 중심축 속성을 정해줬다.
하지만 위치는 알 수 있지만, 파란색 뷰의 크기를 알 수가 없다.
Autolayout은 에러를 띄울 것이다. ❌
여기서 만약 Top과 Leading을 추가해준다면?
에러가 뜨지 않는다. ✅
Bottom과 Trailing이 정해지지 않았는데 왜 에러가 안 뜰까?
왜냐하면, Leading과 Center X를 가지고 Trailing을 계산할 수 있기 때문이다.
Center X는 말그대로 딱 중간 지점이다. Leading과 Center X의 거리만큼 오른쪽으로 가면 Traling의 위치가 된다.
마찬가지로 Bottom도 자동으로 구할 수 있다.
물론 여기서 Width / Height를 주어도 Autolayout을 만족시킬 수 있다. ✅
이 상태에서 Leading 조건이 없어진다면?
너비만 가지고는 위치를 계산할 수 없기 때문에 오토레이아웃은 다시 혼란에 빠진다. ❌
요약하면, 속성 조건이 뷰의 위치/크기를 모두 알려주는 조합은 다음과 같다.
X축 기준으로 Leading, Trailing, Width, Center X 이렇게 4가지 속성이 있다.
이 중 2개가 정해져야 한다. (Y축도 동일하다.)
- 테두리 속성 1개 (Leading or Trailing) + 테두리 속성 1개 (Leading or Trailing)
- 테두리 속성 1개 (Leading or Trailing) + 너비 속성 (Width)
- 테두리 속성 1개 (Leading or Trailing) + 중심축 속성 (Center X)
- 너비 속성 (Width) + 중심축 속성 (Center X)
이게 머릿속에서 자동적으로 떠오를 정도가 된다면,
오토레이아웃의 에러를 쉽게 해결할 수 있다.
이번 글에서는 오토레이아웃의 핵심 개념을 쉽게 설명해보았다.
물론 오토레이아웃을 정복하려면 아직은 조금 부족하다.
실제 화면을 보고 필요한 조건을 추론해내고,
조건이 충돌하거나 부족할 때 어떻게 해결해야하는지도 알아야 한다.
실전편은 다음 글에서 마저 다뤄보도록 하겠다.
어떤 뷰 속성 = a * 다른 뷰 속성 + b
이라는 방정식으로 되어있다.
재밌게 잘읽었습니다~