본문 바로가기

학습 노트/iOS (2021)

154 ~ 155. Auto Layout and Frame-based Layout & Autoresizing Mask

Auto Layout

해상도

iPhone 3GS Portrait Only 320 * 480px
iPad 1 Gen Portait & Landscape 768 * 1024px
iPhone 4 Portait & Landscape 320 * 480pt, 640 * 960px(Retina Display)
iPad 3 Gen Portait & Landscape 768 * 1024pt, 1536 * 2048px (Retina Display)

아이폰이 처음 나왔을 당시 Portrait 모드만 존재하고, 단일 해상도를 가지고 있었다.
이후 아이패드가 출시되면서 Portrait 모드와 Landscape 모드로 구분되고 새로운 해상도에 대한 대책이 필요했다.
하지만 당시에는 아이폰 앱과 아이패드 앱을 별도로 구분 지어 개발했기 때문에 큰 문제가 되지 않았다.

아이폰4는 Retina Display를 탑재한 채 출시됐다.
해상도가 높아진 것에 대한 소비자의 반응은 폭발적이었지만 개발자와 디자이너들은 구분된 두 개의 해상도를 고려해야 하는 부담이 생겼다.
이때부터 픽셀 대신 포인트를 단위로 사용하게됐고, Retina 탑재 기기들을 2배 스케일로 구분하기 시작했다.
즉, 320 * 480px의 해상도가 2배 스케일에서는 640 * 960px이고,
코드는 이를 포인트로 환산해 320 * 480pt로 작동하면 문제가 없었다.
실행되는 기기에 따라 실제 픽셀로 자동으로 mapping했기 때문이다.
이때까지도 기존에 비해 신경 쓸 게 많아졌다 뿐이지 Android와 비교하면 파편화가 적은 수준이었다.

이후 아이패드에서도 Retina Display를 탑재했지만 아이폰과 동일하게 포인트 단위를 사용하고
해상도에 맞춰 Resource를 두 개씩 준비하면 간단했다.

지원해야 할 해상도는 4개이지만,
320 * 480pt, 768 * 1024pt로 두 가지밖에 되지 않기 때문에 조금 번거로울 뿐 문제 되지 않았다.

iPhone 3GS Portrait Only 320 * 480px
iPad 1 Gen Portait & Landscape 768 * 1024px
iPhone 4 Portait & Landscape 320 * 480pt, 640 * 960px(Retina Display)
iPad 3 Gen Portait & Landscape 768 * 1024pt, 1536 * 2048px (Retina Display)
iPhone 5 Portait & Landscape 320 * 568pt

하지만 아이폰 5가 출시되면서 화면비가 변경되었고, 상황이 변하기 시작했다.
높이 자체를 코드로 계산하거나 Auto Resize Mask를 사용하는 것 어느것도 만족스럽지 못하게 된다.
이때부터 Auto Layout이 도입된다.

Auto Layout은 우치와 크기를 직접 지정하는 방식 대신 제약을 사용하는 것으로 UI 구현 방식 자체를 바꿨다.
제약을 추가하면 Layout System이 기기의 정보와 조건을 고려해 최종 위치와 크기를 자동으로 계산하고 배치까지 처리한다.

iPhone 3GS Portrait Only 320 * 480px
iPad 1 Gen Portait & Landscape 768 * 1024px
iPhone 4 Portait & Landscape 320 * 480pt, 640 * 960px(Retina Display)
iPad 3 Gen Portait & Landscape 768 * 1024pt, 1536 * 2048px (Retina Display)
iPhone 5 Portait & Landscape 320 * 568pt
iPhone 6 Portait & Landscape 375 * 667pt
iPhone 6+ Portait & Landscape 414 * 736pt (3x Scale)

아이폰 6와 아이폰 6+가 등장하면서 3배 스케일이 등장한다.
해상도, 스케일, 인터페이스 방향을 고려하면 UI 구성시 고려해야 할 부분이 상당히 늘어났다.

iPhone 3GS Portrait Only 320 * 480px
iPad 1 Gen Portait & Landscape 768 * 1024px
iPhone 4 Portait & Landscape 320 * 480pt, 640 * 960px(Retina Display)
iPad 3 Gen Portait & Landscape 768 * 1024pt, 1536 * 2048px (Retina Display)
iPhone 5 Portait & Landscape 320 * 568pt
iPhone 6 Portait & Landscape 375 * 667pt
iPhone 6+ Portait & Landscape 414 * 736pt (3x Scale)
iPhone 7 `` ``
iPhone 7+ `` ``
iPhone 8 `` ``
iPhone 8+ `` ``
iPhone X `` 375 * 812pt (3x Scale)

아이폰 7과 아이폰 8까지는 6들과 동일한 해상도를 가지다가 아이폰 X가 다시 한 번 새로운 해상도를 추가한다.

iPhone 3GS Portrait Only 320 * 480px
iPad 1 Gen Portait & Landscape 768 * 1024px
iPhone 4 Portait & Landscape 320 * 480pt, 640 * 960px(Retina Display)
iPad 3 Gen Portait & Landscape 768 * 1024pt, 1536 * 2048px (Retina Display)
iPhone 5 Portait & Landscape 320 * 568pt
iPhone 6 Portait & Landscape 375 * 667pt
iPhone 6+ Portait & Landscape 414 * 736pt (3x Scale)
iPhone 7 `` ``
iPhone 7+ `` ``
iPhone 8 `` ``
iPhone 8+ `` ``
iPhone X `` 375 * 812pt (3x Scale)
iPad Pro 10.5" `` 834 * 1112pt (2x Scale)
iPad Pro 12.9" `` 1024 * 1366pt (2x Scale)

아이패드 또한 동일한 해상도를 유지하다 프로 라인업과 함께 새로운 해상도가 추가된다.

DeviceDimensions (portrait)  
12.9" iPad Pro 1024x1366 pt (2048x2732 px @2x)
11" iPad Pro 834x1194 pt (1668x2388 px @2x)
10.5" iPad Pro 834x1194 pt (1668x2388 px @2x)
9.7" iPad Pro 768x1024 pt (1536x2048 px @2x)
7.9" iPad mini 768x1024 pt (1536x2048 px @2x)
10.5" iPad Air 834x1112 pt (1668x2224 px @2x)
9.7" iPad Air 768x1024 pt (1536x2048 px @2x)
10.2" iPad 810x1080 pt (1620x2160 px @2x)
9.7" iPad 768x1024 pt (1536x2048 px @2x)
iPhone 13 Pro Max 428x926 pt (1284x2778 px @3x)
iPhone 13 Pro 390x844 pt (1170x2532 px @3x)
iPhone 13 390x844 pt (1170x2532 px @3x)
iPhone 13 mini 375x812 pt (1125x2436 px @3x)
iPhone 12 Pro Max 428x926 pt (1284x2778 px @3x)
iPhone 12 Pro 390x844 pt (1170x2532 px @3x)
iPhone 12 390x844 pt (1170x2532 px @3x)
iPhone 12 mini 375x812 pt (1125x2436 px @3x)
iPhone 11 Pro Max 414x896 pt (1242x2688 px @3x)
iPhone 11 Pro 375x812 pt (1125x2436 px @3x)
iPhone 11 414x896 pt (828x1792 px @2x)
iPhone XS Max 414x896 pt (1242x2688 px @3x)
iPhone XS 375x812 pt (1125x2436 px @3x)
iPhone XR 414x896 pt (828x1792 px @2x)
iPhone X 375x812 pt (1125x2436 px @3x)
iPhone 8 Plus 414x736 pt (1080x1920 px @3x)
iPhone 8 375x667 pt (750x1334 px @2x)
iPhone 7 Plus 414x736 pt (1080x1920 px @3x)
iPhone 7 375x667 pt (750x1334 px @2x)
iPhone 6s Plus 414x736 pt (1080x1920 px @3x)
iPhone 6s 375x667 pt (750x1334 px @2x)
iPhone 6 Plus 414x736 pt (1080x1920 px @3x)
iPhone 6 375x667 pt (750x1334 px @2x)
4.7" iPhone SE 375x667 pt (750x1334 px @2x)
4" iPhone SE 320x568 pt (640x1136 px @2x)
iPod touch 5th generation and later 320x568 pt (640x1136 px @2x)

최종적으로 지원해야 하는 해상도의 목록은 위와 같지만,
모두 기억하거나 외울 필요 없이 Auto Layout만으로 모든 해상도에서 의도한 UI를 만들 수 있다.

UI 구성 방식

  • Frame-based Layout

가장 먼저 사용된 방식으로 Frame은 상위 View의 좌표 시스템에서 특정 View의 위치와 크기를 나타낸다.
해당 방식에서는 개발자가 Frame을 직접 계산하고 설정해야 한다.
하나의 해상도만 존재하던 때에는 문제가 없었지만 해상도가 점점 늘어나면서 이를 계산하기 위한 과정이 복잡해지기 시작했다.
특히 상위 View의 크기 변화에 따라 하위 View의 크기를 변경하는 작업이 굉장히 어려워졌다.

  • Auto resizing mask

Frame based Layout에서 상위 View와 하위 View의 크기를 연관 지어 변경하는 것이 어려워 도입된 방법이다.
상위 View의 Frame이 변경될 때 하위 View의 Frame이 변경되는 규칙을 미리 지정한다.
이전에 비해 직접 계산해야 하는 부담은 줄었지만 모든 문제를 해결하지는 못했다.

  • Auto Layout

위의 두 방법의 보완 책으로 나온 방법이다.
그렇다고 위의 두 방법을 완전히 대체하는 방법도 아니다.
여전히 Frame을 계산해야 할 필요가 있고, 이를 통해 Auto Layout으로는 해결할 수 없는 문제에 대처할 수 있다.
Auto resizing mask도 여전히 UI 프로토타입에 활용되고 있다.

UI를 구성하는 View의 크기와 위치를 다른 요소와의 관계를 나타내는 규칙을 통해 자동으로 계산하고 대체한다.
이러한 규칙들을 '제약(Constraints)'이라고 하고, Auto Layout의 핵심이다.

이를테면 왼쪽의 Frame based 방식에선 x, y 좌표를 지정하고, 너비와 높이를 직접 지정한다.
반면 오른쪽의 Auto Layout 방식에선 왼쪽 여백과 상단 여백을 50으로 지정하고, 너비와 높이를 100으로 고정하는 제약을 추가한다.
이러한 제약을 기반으로 최종 Frame을 계산하고 배치한다.
유사하지만 이 둘은 큰 차이를 가진다.

  • Adaptive Layout

아이폰 8 출시 이후로 도입된 방식으로 모든 해상도와 기기에 대응하는 하나의 UI를 만들 수 있도록
Size Class와 Trait Collection을 도입했다.
이전까지는 아이폰용 Storyboard와 아이패드용 Storyboard를 구분해 사용했지만
이 이후로는 통합된 Univeral Storyboard에서 개발한다.
이 의미는 개발자가 더 이상 아이폰과 아이패드를 구분할 필요가 없어졌다는 의미로, HW에 따라 해상도를 구분하는 것이 의미가 없어졌다.
또한, 인터페이스 방향을 Portrait와 Landscape로 구분 짓는 것도 구식이 되었다.
Size Class를 사용해 UI를 표시할 수 있는 영역을 큰 틀에서 구분하고,
Trait Collection을 통해 상세한 인터페이스 환경을 구분하는 식으로 변경됐다.

이제부터 Univeral Storyboard에서 Auto Layout, Size Class, Trait Collection을 활용해 UI를 구성한다.
Interface Builder에서 위지윅 방식으로 구현하는 방법과 Code를 통해 구현하는 방식을 모두 숙달한다.

 

Frame-based Layout & Autoresizing Mask

이 둘을 묶어 Springs & Stretch라고 부르기도 한다.
이는 Auto Layout과 대비되는 용어로 사용된다.
Auto Layout을 주로 사용하는 요즘엔 둘을 사용하는 경우가 드물지만 여전히 활용하는 경우가 있어 알고 있어야 한다.

Scene에 View를 하나 추가하고 Size Inspector를 보면 위치와 크기를 입력할 수 있는 필드가 존재한다.
x, y를 각각 30으로, width와 height를 100으로 입력한다.

그러면 지정한 값으로 View가 변하게 된다.
Springs & Stretch에서는 해당 필드에 입력한 값들이 Design Time의 Frame 인 것과 동시에 Run Time의 Frame이다.
지금처럼 별도의 제약을 추가하지 않은 상태에서도 마찬가지로 작용한다.
Interface Builder에서 View를 추가하고 아무런 제약을 추가하지 않는다면 View의 Frame을 기반으로 제약을 자동으로 추가해 준다.
이러한 제약을 '프로토타이핑 제약(Prototyping Constraint)'라고 부른다.
만약 View에 제약을 직접 추가한다면 Size Inspector에서 입력한 Frame 값은
Design Time의 Frame일 뿐 Run Time Frame은 될 수 없고, 프로토타이핑 제약도 추가되지 않는다.

즉, Auto Layout에서 Frame 값을 결정하는 주체는 Auto Layout이다.
따라서 Frame을 변경하고 싶다면 Size Inspector에서 Frame을 수정하는 것이 아니라 관련된 제약을 추가해야 한다.

Frame을 변경했던 필드 아래에 Auto resizing이 존재한다.
이는 두 개의 영역으로 구분되어있는데 왼쪽에서 붉은색 가이드를 통해 Autoresizing Mask를 설정하고,
오른쪽에서 설정된 mask에 따라 View의 Frame이 어떻게 변하는지 미리보기를 통해 보여준다.

기본 상태에선 왼쪽과 상단에 활성화되어있고, View의 두 여백이 현재 값으로 고정된다.
오른쪽과 하단은 활성화되어있지 않아 이 경우 상위 View의 크기에 따라 여백이 자동으로 변경된다.
해당 i빔들은 각각 Left Margin, Right Margin, Top Margin, Bottom Margin으로 부른다.
붉은색 실선으로 활성화됐을 경우 Fixed Margin, 점선으로 비활성화됐을 경우 Flexible Margin을 의미한다.

사각형 내부에는 두 개의 화살표가 존재하는데 이는 너비와 높이를 의미한다.
점선으로 비활성화됐을 경우 고정된 크기로 표시되고, 붉은 실선으로 활성화 됐을 경우 상위 View의 크기에 맞춰 자동으로 변경된다.

위와 같이 View를 배치하고 iPhone 8과 8+ 에서 실행하면

이렇게 다른 결과를 볼 수 있다.
왼쪽과 상단의 여백은 고정이 되어 너비가 좁은 기기에선 잘리고, 너비가 넓은 기기에선 정상적으로 표시된다.
오른쪽 여백이 동일하도록 Autoresize Mask를 변경해 본다.

이번엔 Left Margin을 제거하고 Right Margin을 추가했다.

이번엔 두 기기에서 동일한 방식으로 View가 표시된다.

이번에는 View를 Header의 배경으로 사용한다고 가정한다.
View의 Left, Right, Top의 여백을 0으로 고정하고, 높이는 100, 너비는 화면에 따라 변경되도록 구현한다.

위와 같이 Frame을 변경하고

Left, Top, Right의 Margin을 고정한 뒤, width를 활성화한다.

두 기기 모두 의도한 대로 표시되고, Landscape 모드에서도 의도한 대로 표시된다.

이번엔 View로 화면 전체를 채워본다.

Frame을 조절해 화면 전체를 채우고,

모든 Margin과 너비, 높이를 활성화한다.
이렇게 되면 화면의 크기에 따라 변경된다.

의도한대로 화면 전체를 채우고 있다.

Autoresize Mask는 비교적 단순한 UI 구성에서는 Auto Layout에 비해 시간을 절약할 수 있는 장점이 있지만,
UI가 복잡해질수록 원하는 결과를 얻기 어려워지고, 코드를 사용한 Frame 계산을 함께 사용해야 한다.
실제로 이를 사용하게 되는 경우는 드물지만 UI Prototyping에서 활용할 수 있는 좋은 방법이기 때문이다.
UI를 구성할 때 모든 제약을 오류 없이 추가하는 것은 매우 어렵고 오래 걸린다.
중간에 변경된다면 새로운 제약을 추가하거나 수정하는 과정에서 제약 오류를 일으킬 수 있고,
기존 제약을 모두 제거하고 다시 추가하는 것도 효율적이지 못하다.
이러한 경우 UI 구성이 확정되기 전까지 Autoresizing Mask를 사용할 수 있다.
짧은 시간에 UI의 결과를 확인할 수 있고. 수정에 필요한 시간도 상대적으로 짧다.
또한, 확정된 제약을 추가하면 자동으로 무시되기 때문에 제약과 충돌하지 않고, 일일이 삭제할 필요도 없다.