Roen의 iOS 개발로그

layoutMarginsGuide 파헤치기

by Steady On

iOS

서론

SuperView와 SubView 사이에 안쪽 padding을 주고 싶을 때 사용하는 layoutMarginsGuide에 대해 알아보겠습니다.

 

layoutMarginsGuide

 

https://developer.apple.com/documentation/uikit/uiview/1622651-layoutmarginsguide

 

layoutMarginsGuide | Apple Developer Documentation

A layout guide representing the view’s margins.

developer.apple.com

 

공식문서를 봐도 내용이 아주 심플하네요?

 

A layout guide representing the view’s margins.
Use this layout guide’s anchors to create constraints with the view’s margin.

뷰의 여백을 나타내는 레이아웃 가이드입니다.
이 레이아웃 가이드의 앵커를 사용하여 뷰의 여백에 대한 제약 조건을 만듭니다.
 

 

그럼 이게 어디서 나온건지 한번 알아볼까요?

 

Constrain to margins

스토리보드로 레이아웃을 잡을 때 Constrain to margins 라는 체크박스를 보신적 있으실거에요. 이건 ChildView의 Constrains를 잡을 때 의미있게 작동하게 되는데요.

스토리보드의 Add New Constrains 창

먼저 적당히 회색의 UIView를 SafetyArea 내부에 꽉차게 채워보겠습니다.

바탕view 깔기

그리고 처음에는 체크박스를 활성화 하지 않고 orange색의 UIView를 ChildView로 추가해보겠습니다.

Constrain to margins를 비활성화한 상태로 ChildView 추가

그럼 다음으로는 활성화한 친구를 넣어봐야겠죠?

Constrain to margins를 활성화한 상태로 ChildView 추가

짜잔! 차이가 보이시나요? 똑같이 leading과 trailing을 0으로 설정했는데 옵션을 활성화 해준 view는 왠지 여백이 생겼네요?

Constraints

옆에 Document Outlet 창을 열어서 Constraints가 어떻게 적용되었는지 살펴보면, 옵션을 활성화해주지 않은 오렌지색의 View는 View.leading = leading 옵션이, 인디고색의 View는 View.leading = leadingMargin 옵션으로 차이가 있다는 것을 알 수 있어요.

 

그럼 이 margin은 어디서 조절할 수 있을까요? 답은 Xcode 우측의 Size Inspector 영역에 있습니다.

스크린샷에서 Layout Margins가 Default로 설정된게 보이시나요? 드롭다운 메뉴를 살펴보면, Default, Language Directional, Fixed 3개의 옵션을 볼 수 있습니다. Default는 당연히 기본으로 설정된 값이겠죠? 공식문서를 살펴 볼 것도 없이 일단 Fixed로 바꿔보면 기본값을 알 수 있어요.

Layout Margins

오른쪽 캡처를 보시면 Top, Bottom, Left, Right의 기본값이 모두 8로 설정되었다는 것을 알 수 있네요.

 

근데 여기서 한가지 의문이 들지 않으시나요? 보통 우리가 레이아웃을 잡을 때는 Left, Right라는 단어보단 Leading, Trailing이라는 단어를 사용하잖아요. 근데 왜 여기는 Left, Right일까요? 그럼 두번째 옵션도 한번 선택해서 비교해봅시다.

두 옵션의 비교샷

Language Directional로 변경했더니 드디어 우리에게 익숙한 Leading과 Trailing이 나타났어요. 둘다 똑같이 Layout Margins를 적용하는건데 왜 하나는 Left/Right를 사용하고, 다른 하나는 Leading/Trailing을 사용하는 걸까요?

 

해답은 공식문서에 있습니다!

 

layoutMargins 그리고 directionalLayoutMargins

먼저, layoutMargins부터 살펴봅시다.

layoutMargins: 뷰에 콘텐츠를 배치할 때 사용할 기본 간격입니다.

 

공식문서에서 발췌한 내용을 번역해볼까요.

뷰 컨트롤러의 루트 뷰의 경우 이 속성의 기본값은 시스템 최소 여백과 SateArea inset을 반영합니다. 뷰 계층 구조의 다른 하위 뷰의 경우 기본 레이아웃 여백은 일반적으로 각 측면에서 8point지만 뷰가 SateArea 내에 완전히 포함되지 않거나  presentsSuperviewLayoutMargins 속성이 true인 경우 값은 더 커질 수 있습니다. 

이 속성은 뷰 가장자리와 하위 뷰 사이의 원하는 공간 크기(포인트로 측정)를 지정합니다. 자동 레이아웃은 여백을 콘텐츠 배치의 신호로 사용합니다. 예를 들어, 형식 문자열 "|-[subview]-|"를 사용하여 수평 제약 조건 세트를 지정하는 경우 하위 뷰의 왼쪽 및 오른쪽 가장자리는 해당 레이아웃 여백만큼 상위 뷰 가장자리에서 삽입됩니다.

SuperView 내부에 레이아웃을 배치하지 않을 여백!이에요. 근데 링크 들어가보신분은 알겠지만, 첫문단에 대한 내용이 빠져있죠? 첫번째 문단은 directionalLayoutMargins에 대한 이야기라 제가 의도적으로 잠시 생략을 했는데요. 같이 살펴볼까요?

iOS 11 이상에서는 이 속성 대신 directionLayoutMargins 속성을 사용하여 레이아웃 여백을 지정합니다. directionLayoutMargins 속성의 leading 및 trailing edge inset은 이 속성의 left 및 right inset과 동기화됩니다. 예를 들어, leading directional edge inset을 20포인트로 설정하면 왼쪽에서 오른쪽으로 쓰는 언어를 사용하는 시스템에서 이 속성의 left inset이 20포인트로 설정됩니다. 

일단 directionLayoutMargins의 leading은 layoutMargins의 left와, trailing은 right와 동기화 된다고 하네요? 바로 위 발췌의 맨 마지막 문장을 보면 왜 갑자기 leading, trailing의 개념이 생겨났는가에 대해서 이해할 수 있게 됩니다. 우리나라를 비롯해서 왼쪽에서 오른쪽 방향으로 글이나 Flow가 흘러가는 문화권이 있으면, 반대로 왼쪽에서 오른쪽 방향으로 Flow가 흘러가는 문화권도 있을거에요.

 

HIG의 Left to Right라는 문서를 볼까요?

https://developer.apple.com/design/human-interface-guidelines/right-to-left

 

Right to left | Apple Developer Documentation

Support right-to-left languages like Arabic and Hebrew by reversing your interface as needed to match the reading direction of the related scripts.

developer.apple.com

관련 스크립트의 읽기 방향과 일치하도록 필요에 따라 인터페이스를 반전하여 아랍어 및 히브리어와 같은 오른쪽에서 왼쪽으로 쓰는 언어를 지원합니다.

앱이 글로벌 문화권을 타겟으로 만들어지는 경우에는 반드시 사용하는 국가에 따라 인터페이스가 반전될때가 필요한데요. 이걸 매번 분기처리해야한다면 개발자에게 너무 가혹하겠죠? 그래서 애플에서는 Left/Right 대신 leading/trailing이라는 속성을 통해 개발자가 따로 설정해주지 않아도 앱에서 자동으로 Flow를 반전시킬 수 있도록 준비해둔거에요. 그래서 Left, Right라고 명시해버리면, 프레임워크에서는 '아 이건 문화권에 상관없이 왼쪽/오른쪽을 고정해야 하는 거구나!'로 인식해버려요. 그래서 추가된게 directionalLayoutMargins라는 겁니다!

그럼 우리는 앞으로 사용해야 할 directionalLayoutMargins에 대해 알아봐야겠죠?

 

https://developer.apple.com/documentation/uikit/uiview/2865930-directionallayoutmargins

 

directionalLayoutMargins | Apple Developer Documentation

The default spacing to use when laying out content in a view, taking into account the current language direction.

developer.apple.com

잘 보시면, layoutMargins의 설명에 "현재의 언어방향을 고려하여"라는 말만 추가되었다는 것을 알 수 있어요. 더 읽어봐도 본문은 layoutMargins와 내용이 거의 동일하네요! 즉, 둘의 차이점은 문화적인 언어방향성을 고려하냐 안하냐의 차이에요! 하지만, 공식문서에도 적혀 있다시피 iOS 11 이상부터는 directionalLayoutMargins를 사용하라고 하니 혹시 margin을 사용할 때가 있다면, 꼭 directionalLayoutMargins을 사용합시다!

 

layoutMarginsGuide에 따라 layout 잡기

일단, 위에서 layoutMargins와 directionalLayoutMargins를 알아봤는데요. 그럼 헷갈리실 수도 있을것 같아요. "그럼 레이아웃을 적용할때도 뭔가 directionalLayoutMarginsGuide 뭐 그런걸로 잡아줘야 하나?" 네, 사실 제얘기구요...ㅎㅎ

결론부터 말하면 그럴 필요가 없습니다!!

 

위에서 살펴본 Size Inspector 영역 내에서도 두개가 분리되어 있는게 아니라 드롭다운으로 선택하는 방식이었잖아요? layoutMarginsGuide는 두가지의 layoutMargins 중에 한가지를 반영해요. 누가 더 먼저랄 것도 없고 그냥 스코프 내의 코드 순서상 가장 마지막에 할당된 값을 반영한다는 말이에요.

 

UIView 내부에 Label을 subview로 넣어주고, layoutMarginsGuide와 레이아웃을 맞춰볼까요?

NSLayoutConstraint.activate([
    self.label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
    self.label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
    self.label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
    self.label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
])
 

아무 값도 설정하지 않았더니 기본값인 8만큼 margin이 들어간 것을 볼 수 있어요. 그럼 두가지 layoutMargins에 값을 할당해볼게요. 

// 왼쪽 캡처
layoutMargins = .init(top: 8, left: 8, bottom: 8, right: 8)
directionalLayoutMargins = .init(top: 16, leading: 16, bottom: 16, trailing: 16)

// 오른쪽 캡처
directionalLayoutMargins = .init(top: 16, leading: 16, bottom: 16, trailing: 16)
layoutMargins = .init(top: 8, left: 8, bottom: 8, right: 8)

왼쪽 캡처만 보면 directionalLayoutMarginsGuide이 우선적용되나 싶겠지만, 오른쪽 캡처를 보면 결국 마지막에 할당된 값이 들어간다는걸 알 수 있겠죠? 그러니까 그냥 굳이 꼭 오른쪽 왼쪽이 지정되어야 하는게 아니라! 문화권을 고려하면서 UI를 만들고 싶다면, 그냥 layoutMarginsGuide를 사용해주면 된다는 얘기입니다 ㅎㅎ

 

그리고 혹시나해서 덧붙이는 건데 위의 text 사이즈가 차이나 보이는건 단순 캡처본의 비율 때문입니다! 실제로 text 크기가 작아진건 아니에요!

 

마무리

UILabel의 text 주변에 padding값을 주고 싶어서 custom class를 만들면서 파헤쳐 본 내용을 정리해봤습니다. 알아보기전에는 왜 UIEdgeInset의 매개변수는 왜 leading/trailing이 아니라 left/right인지 궁금했는데, 그런 부분도 어느정도 해결이 된것 같네요! 또, 애플에서는 정말 여러문화권에 대응하는 것을 중요하게 생각하고, 또 그만큼 개발자 입장에서도 편하게 해뒀구나 라고 느꼈습니다!

 

블로그의 정보

Roen의 iOS 개발로그

Steady On

활동하기