Debugging Auto Layout

Panther·2021년 5월 31일
0

Auto Layout Guide

목록 보기
3/5

Types of Errors

오토 레이아웃에서 오류들은 세 가지 주요 카테고리로 나눌 수 있습니다.

  • 조건을 충족하지 않는 레이아웃. 레이아웃이 유효한 솔루션을 갖지 않고 있습니다. Unsatisfiable 레이아웃에서 더 많은 정보를 살펴볼 수 있습니다.

  • 모호한 레이아웃. 레이아웃은 둘 혹은 이상의 가능한 솔루션이 있습니다. 더 많은 정보는 Ambiguous Layouts에서 볼 수 있습니다.

  • 논리적 오류. 레이아웃 로직에 버그가 있습니다. 더 자세한 정보는 Logical Errors를 살펴보시기 바랍니다.

Unsatisfiable Layouts, Ambiguous Layouts, Logical Errors 세 가지는 아래 내용에 나옵니다.

대부분의 경우 실제 문제는 무엇이 잘못되었는지를 판단하는 것입니다. 필요한 제약들을 추가했을 것입니다. 그러나 앱을 실행했을 때 바라던 것처럼 결과가 나오지 않을 수 있습니다.

보통 문제를 이해한다면 해결책은 명확합니다. 충돌하는 제약들을 제거하고, 놓친 제약들을 추가합니다. 그리고 우선순위를 조정합니다. 물론 문제를 쉽게 이해할 수 있는 부분을 찾는 것은 시행착오와 오류가 있을 것입니다. 다른 기술처럼 연습을 하다보면 쉬워질 것입니다.

그러나 가끔은 더 복잡할 수 있습니다. 이는 Debugging Tricks and Tips에서 살펴볼 수 있습니다.

Debugging Tricks and Tips는 이 글의 뒤에 나옵니다.

Unsatisfiable Layouts

조건을 충족시키지 못하는 레이아웃은 시스템이 현재 제약들에서 유효한 솔루션을 찾지 못할 때 발생합니다. 둘 혹은 이상의 필요한 제약들이 충돌합니다. 왜냐하면 모두가 동시에 논리적으로 맞을 수 없기 때문입니다.

Identifying Unsatisfiable Constraints

인터페이스 빌더는 보통 디자인 타임에 충돌을 감지할 수 있습니다. 이와 같은 경우 인터페이스 빌더는 몇 가지 방식으로 오류를 표시합니다.

  • 충돌하고 있는 모든 제약들은 캔버스에 빨간색으로 그려집니다.

  • Xcode는 이슈 네비게이터에서 경고로 충돌하고 있는 제약들을 목록으로 만듭니다.

  • 인터페이스 빌더는 document outline의 오른쪽 위에 빨간색 원 안에 화살표가 있는 모양을 표시합니다.

현재 레이아웃에서 모든 Auto Layouts 이슈들을 목록으로 표시할 수 있도록 화살표를 클릭하시기 바랍니다.

인터페이스 빌더는 이 이슈들과 관련해 수정할 수 있는 방법을 추천합니다. 더 많은 정보는 Auto Layout Help에 있는 Resolving Layout Issues for a View Controller, Window, or Root View를 살펴보시기 바랍니다.

NOTE
인터페이스 빌더에서 제공하는 즉각적인 피드백이 유효한 레이아웃을 쉽게 생성해줄 수 있을지라도, 모든 레이아웃 오류를 찾을 수 있는 것은 아닙니다.
예를 들어 인터페이스 빌더는 캔버스의 현재 크기에서만 충돌을 감지할 수 있습니다. 그러나 몇 가지 충돌들은 루트 뷰가 특정 포인트를 넘어서 확장되고 있거나 줄어들고 있을 때 발생합니다(혹은 컨텐트가 특정 포인트를 넘어서 확장되거나 줄어들 때). 인터페이스 빌더는 이와 같은 오류들은 감지할 수 없습니다.
그렇기 때문에 인터페이스 빌더에서 확인할 수 있는 모든 이슈들을 수정해야 할지라도, 문제가 있는 확실한 에러들을 수정하는 것은 충분하지 않을 수 있습니다. 그렇기 때문에 스크린 사이즈의 전체 범위, 오리엔테이션, 동적 타입 크기, 지원하려고 하는 언어에 걸쳐 런타임 테스팅을 수행할 필요가 있습니다.

시스템이 런타임에 조건을 충족하지 않는 레이아웃을 감지할 때, 시스템은 아래 단계를 수행합니다.

  1. Auto Layout은 충돌하고 있는 제약들 집합을 확인합니다.

  2. 충돌하고 있는 제약 중 한 가지를 깨고 레이아웃을 확인합니다. 시스템은 유효한 레이아웃을 찾을 때까지 제약을 깨는 것을 계속합니다.

  3. Auto Layout은 콘솔에 충돌하는 제약과 깨진 제약들을 로그 정보로 표시합니다.

이와 같은 대비 시스템은 사용자에게 의미있는 무엇인가를 제공하는 것을 시도하는 동안 앱이 진행할 수 있도록 합니다. 그러나 제약을 깨는 것의 효과는 레이아웃마다 달라질 수 있습니다. 혹은 빌드에 따라서도 달라질 수 있습니다.

여러 경우에 놓친 제약들은 시각적 효과가 없을 것입니다. 뷰 계층구조는 의도한 설계처럼 나타납니다. 다른 경우 놓친 제약은 뷰 계층구조의 전체 섹션을 놓여져있지 않거나 크기가 잘못되어 있거나 혹은 완전히 사라지도록 만들 수 있습니다.

명확한 효과가 없을 때 가끔 오류들을 무시할 수 있습니다. 결국 앱의 움직임을 바꾸지 않습니다. 그러나 뷰 계층구조 혹은 SDK에 생기는 모든 변화는 명확하게 깨진 레이아웃을 제공하면서 깨진 제약의 집합으로 바꿀 수 있습니다.

그러므로 조건을 충족하지 못하는 제약 오류를 항상 수정해줘야 합니다. 테스팅을 하는 동안 명확하지 않은 오류들을 확실하게 잡아낼 수 있도록 UIViewAlertForUnsatisfiableConstraints에서 symbolic breakpoint를 설정해야 합니다.

Preventing Unsatisfiable Constraints

조건을 충족시키지 못하는 제약은 상대적으로 고치기가 쉽습니다. 시스템은 조건을 충족시키지 못하는 제약들이 발생할 때 알려줄 것이며, 충돌하는 제약들의 목록을 제공합니다.

오류를 파악하자마자 솔루션은 보통 간단합니다. 제약들 중 한 가지를 지우거나 선택적 제약으로 바꾸면 됩니다.

그러나 더 자세하게 알아야 하는 몇 가지 일반적인 이슈가 있습니다.

  • 조건을 충족시키지 못하는 제약들은 보통 뷰 계층구조에 뷰를 코드로 추가할 때 발생합니다.
    기본값으로 새로운 뷰들은 translatesAutoresizingMaskIntoConstraints 속성이 YES로 설정됩니다. 인터페이스 빌더는 캔버스에 뷰의 제약을 그리기 시작할 때 자동으로 이 속성을 NO로 설정합니다. 그러나 코드로 뷰를 생성하고 레이아웃을 설정하면, 코드로 제약들을 추가하기 전에 이 속성을 NO로 설정해야 합니다.

  • 조건을 충족시키지 않는 제약들은 보통 뷰 계층구조가 너무 작은 공간에 표현되려고 할 때 발생합니다.
    보통 뷰가 공간에 접근하려고 하고 레이아웃을 적합한 방식으로 디자인하려고 할 때 공간의 최소 크기를 예견할 수 있습니다. 그러나 internationalization과 동적 타입은 뷰의 컨텐트가 기대한 것보다 더 크게 만들어줄 수 있습니다. 가능한 순열의 수가 증가하면, 레이아웃이 모든 상황에 작동하는 것을 보장하기 어려워질 수 있습니다.
    대신 레이아웃이 예측가능하고 제어가능할 수 있는 선에서 실패할 수 있도록 실패 지점이 있는 빌드를 원할 수 있습니다.
    필수로 하는 제약을 높은 우선순위에 있는 것으로 전환하는 것을 생각해볼 수 있습니다. 이 제약들은 레이아웃이 충돌을 발생할 대 깨지는 것을 제어할 수 있도록 해줍니다.
    예를 들어 실패 지점을 999 우선순위로 설정할 수 있습니다. 대부분의 경우에서 이렇게 높은 우선순위를 갖는 제약은 필수적인 제약인 것처럼 설정됩니다. 그러나 충돌이 발생하면 남은 레이아웃은 보호하면서 높은 우선순위의 제약은 깨집니다.
    유사하게 내재된 컨텐트 크기를 갖는 뷰에 필수 우선순위 값을 갖는 content-hugging 혹은 compression-resistance 속성을 설정하는 것은 피해야 합니다. 보통 컨트롤의 크기는 이상적인 실패 지점과 같은 역할을 합니다. 컨트롤은 레이아웃에 의미있는 영향을 미치지 않는 채로 더 크거나 작아질 수 있습니다.
    내재된 컨텐트 크기로써만 표시되어야 하는 컨트롤들이 존재합니다. 그러나 이와 같은 경우에 레이아웃이 예측가능하지 않은 방법으로 깨지도록 하는 것보다 몇 포인트 떨어져 있도록 하는 것이 더 낫습니다.

Ambiguous Layouts

모호한 레이아웃은 제약의 시스템이 둘 혹은 이상의 유효한 솔루션을 갖고 있을 때 발생합니다. 주요 원인으로 두 가지가 있습니다.

  • 레이아웃은 모든 뷰에 대해 위치를 고유하게 구체화하는 추가적인 제약이 필요합니다.
    어떤 뷰가 모호한지 결정한 후 뷰의 위치와 크기 모두를 고유하게 구체화하는 제약을 추가해야 합니다.

  • 레이아웃은 같은 우선순위를 갖는 선택적 제약들을 충돌시키고, 시스템은 어떤 제약이 깨져야 하는지 알지 못합니다.
    더이상 같지 않도록 우선순위를 바꾸는 것을 통해 시스템에 어떤 제약이 깨져야 하는지 알려줘야 합니다. 시스템은 가장 낮은 우선순위의 제약을 먼저 깨버립니다.

Detecting Ambiguous Layouts

조건을 충족하지 못하는 레이아웃처럼 언티페이스 빌더는 디자인 타임에 모호한 레이아웃을 감지하고, 수정을 위한 제안을 제공합니다. 이와 같은 모호성은 이슈 네비게이터에서 경고로 나타나고, document outline에 오류로 나타나며, 캔버스에 빨간색 선으로 나타납니다. 더 많은 정보를 원한다면 Identifying Unsatisfiable Constraints를 살펴보시기 바랍니다.

Identifying Unsatisfiable Constraints는 이 글의 앞부분에 등장합니다.

모호한 레이아웃이 런타임에 발생할 때, 오토 레이아웃은 하나의 간으한 솔루션을 선택합니다. 이는 레이아웃이 기대한 것처럼 나타나거나 나타나지 않을 것을 의미합니다. 게다가 콘솔에 경고가 나타나지 않고, 모호한 레이아웃을 위한 브레이크포인트를 설정할 방법이 없습니다.

결과적으로 모호한 레이아웃은 조건을 충족시키지 못하는 레이아웃보다 감지하기도, 확인하기도 어렵습니다. 만약 모호성이 명백함과 시각적 효과를 갖고 있더라도 오류가 모호성 때문인지 레이아웃 로직에서 오류가 있는 것인지 결정하기는 어렵습니다.

다행히도 모호한 레이아웃을 확인할 수 있는 몇 가지 메소드가 존재합니다. 이와 같은 모든 메소드는 오직 디버깅을 위해서만 사용되어야 합니다. 뷰 계층구조에 접근할 수 있는 곳에 브레이크포인트를 설정하고, 콘솔로부터 아래에 해당하는 하나의 메소드를 호출하면 됩니다.

  • hasAmbiguousLayout. iOS와 OS X 모두에서 이용 가능합니다. 잘못 놓여진 뷰에 대해 이 메소드를 호출하면 됩니다. YES를 반환하면 뷰의 프레임이 모호한 것입니다. 그렇지 않은 경우 NO를 반환합니다.

  • exerciseAmbiguityInLayout. iOS와 OS X 모두에서 이용 가능합니다. 모호한 레이아웃을 갖는 뷰에 이 메소드를 호출합니다. 유효한 솔루션들 사이에 시스템을 토글할 것입니다.

  • constraintsAffectingLayoutForAxis:. iOS에서 이용 가능합니다. 뷰에서 이 메소드를 호출합니다. 구체화된 축에 다라 뷰에 영향을 미치는 모든 제약들을 배열로 반환합니다.

  • constraintsAffectingLayoutForOrientation:. OS X에서 이용 가능합니다. 뷰에서 이 메소드를 호출합니다. 구체화된 오리엔테이션에 따라 뷰에 영향을 미치는 모든 제약들의 배열을 반환할 것입니다.

  • _autolayoutTrace. iOS에서 private 메소드로 이용 가능합니다. 뷰에서 이 메소드를 호출합니다. 해당 뷰를 포함하고 있는 전체 뷰 계층구조에 대한 진단 정보를 스트링으로 반환합니다. 모호한 뷰는 레이블이 붙고, translatesAutoresizingMaskIntoConstraints가 YES로 설정된 뷰도 표시됩니다.

콘솔에서 이와 같은 명령어를 실행할 때 Objective-C syntax를 실행하길 원할 것입니다. 예를 들어 브레이크 포인트가 실행을 멈춘 후, call [self.myView exerciseAmbiguityInLayout]을 콘솔 창에 입력해서 exerciseAmbiguityInLayout 메소드를 myView 객체에 호출할 수 있도록 합니다. 유사하게 po [self.myView autolayoutTrace] 입력해 myView를 포함하는 뷰 계층구조에 대한 진단 정보를 프린트할 수 있습니다.

NOTE
앞서 언급한 진단 메소드를 실행하기 전에 인터페이스 빌더에서 발견되는 모든 이슈들을 수정해야 합니다. 인터페이스 빌더는 찾을 수 있는 모든 오류들을 수정하길 시도할 것입니다. 이는 모호한 레이아웃을 찾으면, 레이아웃이 더이상 모호하지 않도록 하기 위해 제약들을 추가합니다.
결국 hasAmbiguousLayout는 NO를 반환합니다. exerciseAmbiguityInLayout는 기대하지 않은 추가적인 제약들을 반환할 것입니다.

Logical Errors

논리적 오류는 간단하게 버그입니다. 무엇인가 잘못되었다고 가정할 수 있습니다. 아마도 오토 레이아웃이 뷰의 프레임을 어떻게 계산하는지에 대한 가정입니다. 아마도 생성한 제약들의 집합에 대한 가정입니다. 아마도 복잡한 움직임을 생성하기 위한 제약들의 상호작용을 어떻게 하는지에 대한 가정입니다. 무관하게 어떤 것이 생각하고 있는 멘탈 모델과 일치하지 않고 있는 것입니다.

논리적 오류가 가장 찾기 어렵습니다. 모든 가능성들을 제거한 후에는 가능성이 없다고 하더라도 논리적 오류여야 합니다. 그러나 버그를 갖고 있다고 결정한 후에도 잘못된 점이 어느 곳에 남아있는지 정확하게 발견해야 합니다.

도구나 단계적인 해결 지시사항은 존재하지 않습니다. 논리적 오류를 수정하는 것은 실험적이고 반복적인 테스트입니다. 이는 어떻게 고칠 수 있는지 문제를 확인하고 파악하는 것입니다. 그러나 도움이 되는 몇 가지 제안이 있습니다.

  • 존재하는 제약들을 재확인합니다. 어떤 제약들을 놓치지 않았는지 혹은 원하지 않은 제약이 우연히 추가되지 않았는지 확실히 해야 합니다. 모든 제약들이 정확한 아이템과 특성에 붙었는지 확실히 해야 합니다.

  • 뷰 프레임을 더블체크합니다. 의도하지 않게 확장되고 있거나 축쇠도고 있지 않은지 확실히 해야 합니다.
    레이블 혹은 버튼처럼 배경이 보이지 않는 뷰에 대해서 특히 중요합니다.이와 같은 아이템들이 의도치 않게 크기가 바뀔 때 명확한 부분이 없을 것입니다.
    크기 재조정의 한 가지 증상은 베이스라인에 정렬된 뷰가 더이상 적절하게 정렬되지 않는 것입니다. 이는 베이스 라인 정렬이 오직 뷰가 내재된 컨텐트 높이로 보여질 때에만 작동하기 때문입니다. 뷰를 수직으로 확장시키거나 축소시키고 있다면, 텍스트는 의도하지 않은 위치에서 이상하게 나타날 것입니다.

  • 만약 컨트롤이 항상 내재된 컨텐트 크기와 일치한다면, content-hugging과 compression-resistance 속성에 매우 높은 값(예를 들어 999)을 설정합니다.

  • 레이아웃을 만들고 있는 모든 가정들을 찾고, 이와 같은 가정들이 옳은 논리인지 확실히할 수 있도록 명확한 제약들을 추가해야 합니다.
    조건을 충족시키지 못하는 제약은 보통 가장 찾기 쉽고 가장 수정하기 쉬운 문데라는 것을 기억하시기 바랍니다. 충돌이 발생할 때까지 추가적인 제약들을 추가하고, 이후 파악한 뒤 충돌을 수정해야 합니다.

  • 주어진 제약들이 왜 보이는 결과를 나타내고 있는지 이해하는 것을 시도해야 합니다. 만약 이해하게 된다면, 수정을 맞는 방법으로 하고 있다는 뜻입니다.

  • 대안이 되는 제약들로 실험해야 합니다. 오토 레이아웃은 보통 같은 문제에 대해 몇 가지 다른 솔루션을 제공할 것입니다. 각자 다른 접근방식으로 시도하는 것은 문제를 수정하거나 최소한 실수를 발견하는 데 더 쉽도록 도와줄 것입니다.

Debugging Tricks and Tips

아래 토픽들은 레이아웃과 더불어 작업 도중 만나게 되는 몇 가지 놀라운 움직임에 대한 설명을 표현하는 정보를 모으고 조직화하기 위해 테크닉들을 소개합니다. 모든 레이아웃에 이 테크닉들을 적용하길 원하지 않을 수 있습니다. 그렇지만 대부분의 어려운 문제를 해결하기 위한 방법을 제공하는데 도움이 될 수 있습니다.

Understanding the Logs

뷰에 대한 정보는 콘솔에 프린트 될 수 있습니다. 이는 조건을 충족하지 않는 레이아웃 때문일 수도 있습니다. 또한, constraintsAffectingLayoutForAxis: 혹은 constraintsAffectingLayoutForOrientation: 디버깅 메소드를 사용해 제약들의 로그를 표현하도록 했기 때문일 수도 있습니다.

각각의 방법에서 이 로그 정보에 매우 많은 유용한 정보를 찾을 수 있습니다. 아래는 조건을 충족시키지 못하는 레이아웃 오류로부터 나오는 샘플 출력 결과물입니다.

2015-08-26 14:27:54.790 Auto Layout Cookbook[10040:1906606] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7a87b000 H:[UILabel:0x7a8724b0'Name'(>=400)]>",
    "<NSLayoutConstraint:0x7a895e30 UILabel:0x7a8724b0'Name'.leading == UIView:0x7a887ee0.leadingMargin>",
    "<NSLayoutConstraint:0x7a886d20 H:[UILabel:0x7a8724b0'Name']-(NSSpace(8))-[UITextField:0x7a88cff0]>",
    "<NSLayoutConstraint:0x7a87b2e0 UITextField:0x7a88cff0.trailing == UIView:0x7a887ee0.trailingMargin>",
    "<NSLayoutConstraint:0x7ac7c430 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7a887ee0(320)]>"
)
 
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7a87b000 H:[UILabel:0x7a8724b0'Name'(>=400)]>
 
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

이 오류 메시지는 다섯 가지 충돌하는 제약들을 보여주고 있습니다. 동시에 모든 제약들의 논리가 참일 수는 없습니다. 한 가지를 제거할 필요가 있거나 선택적 제약으로 바꿔야 할 필요가 있습니다.

다행히 뷰 계층구조는 상대적으로 간단합니다. 레이블과 텍스트 필드를 포함하는 슈퍼뷰를 갖고 있습니다. 충돌하는 제약들은 아래 관계를 설정합니다.

  1. 레이블의 넓이는 400 포인트보다 더 크거나 같습니다.

  2. 레이블의 leading edge는 슈퍼뷰의 leading 마진과 같습니다.

  3. 레이블과 텍스트 필드 사이에 8 포인트 여백이 있습니다.

  4. 텍스트 필드의 trailing edge는 슈퍼뷰의 trailing 마진과 같습니다.

  5. 슈퍼뷰의 넓이는 320 포인트로 설정되어 있습니다.

시스템은 레이블의 넓이 제약을 깨면서 복구하길 시도할 것입니다.

NOTE
제약들은 콘솔에서 Visual Format 언어를 사용해 쓰여집니다. 스스로의 제약을 생성하기 위해 Visual Format 언어를 사용하지 않으려고 할지라도, 효과적으로 Auto Layout 이슈를 디버그하기 위해 이 언어를 읽을 줄 알고 이해할 수 있어야 합니다.

이 제약들 중 마지막은 시스템에 의해 생성되었습니다. 이를 바꿀 수는 없습니다. 게다가 첫 번째 제약에 명확한 충돌을 발생하고 있습니다. 만약 슈퍼뷰가 오직 320 포인트의 넓이를 갖는다면, 400 포인트 넓이의 레이블을 가질 수 없습니다. 다행히 첫 번재 제약을 제거할 필요는 없습니다. 만약 이 제약의 속성 우선선위를 999로 떨어뜨리면, 시스템은 선택된 넓이를 제공하길 시도하려고 할 것입니다. 이는 다른 조건을 만족시키면서 최대한 가깝게 시도하려고 할 것입니다.

뷰의 오토리사이징 마스크(예를 들어 translatesAutoresizingMaskIntoConstraints가 YES인 제약들)에 기반한 제약들은 마스크에 대한 추가적인 정보를 갖습니다. 제약의 주소 이후 로그 스트링은 h= 이후 세 글자가 따라오는 것을 보여줄 것이고, v= 뒤에 세 글자가 따라올 것입니다. A-(하이픈) 글자는 수정된 값을 나타내고, & (ampersand)는 유연한 값(flexible value)을 나타냅니다. 수평 마스크에서 (h=) 세 가지 클자는 왼쪽 마진, 넓이, 오른쪽 마진을 나타냅니다. 수직 마스크에서 (v=) 따라오는 글자는 top 마진, 높이, bottom 마진을 나타냅니다.

예를 들어 아래와 같은 메시지 로그를 생각해보시기 바랍니다.

<NSAutoresizingMaskLayoutConstraint:0x7ff28252e480 h=--& v=--& H:[UIView:0x7ff282617cc0(50)]>"

이 메시지는 아래 파트들로 구성됩니다.

  • NSAutoresizingMaskLayoutConstraint:0x7ff28252e480 제약의 클래스 및 주소입니다. 이 예제에서 클래스는 뷰의 오토리사이징 마스크에 기반하고 있다는 것을 말해주고 있습니다.

  • h=--& v=—& 뷰의 오토리사이징 마스크입니다. 기본값 마스크입니다. 수평에서 고정된 왼쪽 마진을 갖고 있고, 고정값 넓이와 유연한 오른쪽 마진을 갖고 있습니다. 수직에서 고정된 top 마진을 갖고 있고, 고정값 높이와 유연한 bottom 마진을 갖고 있습니다. 다른 말로 뷰의 top 왼쪽 코너와 뷰의 크기가 슈펴뷰의 크기가 변할 때 상수로 남아있다는 말입니다.

  • H:[UIView:0x7ff282617cc0(50)] 제약의 Visual Format 언어 표현입니다. 이 예제에서 한 뷰를 상수 50 포인트의 넓이로 정의합니다. 이 표현은 제약에 의해 영향을 받는 모든 뷰의 클래스와 주소를 포함합니다.

Adding Identifiers to the Logs

이전 예제가 상대적으로 이해하기 쉬웠을지라도, 더 긴 제약들의 목록은 빠르게 따라가기가 어려울 것입니다. 의미있는 identifier를 갖는 뷰와 제약을 제공하는 것을 통해 로그를 더 읽기 쉽게 만들 수 있습니다.

만약 뷰가 명확한 텍스트 구송요소를 갖고 있다면, Xcode는 이를 identifier로 사용합니다. 예를 들어 Xcode는 이와 같은 뷰를 확인할 수 있도록 레이블의 텍스트, 버튼의 타이틀, 혹은 텍스트 필드의 플레이스홀더를 사용합니다. 아이덴티티 인스펙터에서 뷰의 Xcode 레이블을 설정하면 됩니다. 대부분이 콘솔 로그에 표현됩니다.

제약에서는 코드 혹은 특성 인스펙터를 사용해 identifier 속성을 설정할 수 있습니다. 오토 레이아웃은 콘솔에 정보를 표시할 때 이 identifier를 사용할 것입니다.

예를 들어 identifier가 설정된 상태에서 조건을 충족시키지 못하는 제약 오류가 아래처럼 보여질 수 있습니다.

2015-08-26 14:29:32.870 Auto Layout Cookbook[10208:1918826] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7b58bac0 'Label Leading' UILabel:0x7b58b040'Name'.leading == UIView:0x7b590790.leadingMargin>",
    "<NSLayoutConstraint:0x7b56d020 'Label Width' H:[UILabel:0x7b58b040'Name'(>=400)]>",
    "<NSLayoutConstraint:0x7b58baf0 'Space Between Controls' H:[UILabel:0x7b58b040'Name']-(NSSpace(8))-[UITextField:0x7b589490]>",
    "<NSLayoutConstraint:0x7b51cb10 'Text Field Trailing' UITextField:0x7b589490.trailing == UIView:0x7b590790.trailingMargin>",
    "<NSLayoutConstraint:0x7b0758c0 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7b590790(320)]>"
)
 
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7b56d020 'Label Width' H:[UILabel:0x7b58b040'Name'(>=400)]>
 
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

보이는 것처럼 이 identifier는 로그에서 제약들을 빠르고 쉽게 인식할 수 있도록 도와줍니다.

Visualizing Views and Constraints

Xcode는 뷰 계층구조에 있는 뷰와 제약을 시각화할 수 잇도록 도와주는 도구들을 제공합니다.

시뮬레이터를 뷰에서 보기 위해 아래와 단계가 있습니다.

  1. 시뮬레이터에서 앱을 실행합니다.

  2. Xcode로 돌아옵니다.

  3. Debug > View Debugging > Show Alignment Rectangles 순으로 선택합니다. 이 설정은 뷰의 edge들을 설명합니다.

정렬 사각형들은 오토 레이아웃에서 사용되는 edge입니다. 이 옵션을 활성화하는 것은 의도하지 않게 크기가 조절되는 모든 정렬 사각형들을 빠르게 찾을 수 있도록 해줍니다.

만약 더 많은 정보가 필요하다면 Debug View Hierarchy 버튼()을 클릭하시기 바랍니다. 그러면 뷰 계층구조를 탐색할 수 있고 상호작용할 수 있는 몇 가지 도구를 제공하면서 Xcode는 상호작용할 수 있는 뷰 디버거를 보여줄 것입니다. 오토 레이아웃 이슈를 디버깅할 때, “Show clipped content”와 “Show constraints” 옵션은 특히 유용합니다.

“Show clipped content” 옵션을 활성화하면 스크린 밖에 있는 뷰의 위치를 표시합니다. “Show constraint” 옵션을 활성화하면 현재 선택된 뷰에 영향을 주고 있는 모든 제약들을 보여줍니다. 두 옵션 모두 무엇인가 이상하게 작동하기 시작할 때 빠른 검사를 제공합니다.

더 많은 정보는 Debug Area Help에서 볼 수 있습니다.

Debug Area Help
https://help.apple.com/xcode/mac/11.4/

Understanding Edge Cases

오토 레이아웃이 의도하지 않은 방향으로 행동하도록 할 수 있는 몇 가지 edge에 대한 케이스가 아래에 있습니다.

  • 오토 레이아웃 프레임이 아니라 정렬 사각형들을 기준으로 뷰를 위치시킵니다. 대부분 이 값들은 동일합니다. 그러나 몇 가지 뷰는 레이아웃 계산으로부터 뷰의 일부분(예를 들어 badge)을 제외시키기 위해 커스텀 정렬 사각형을 설정할 것입니다.
    더 많은 정보는 UIView Class Reference에 있는 Aligning Views with Auto Layout에서 찾을 수 있습니다.

UIView Class Reference
https://developer.apple.com/documentation/uikit/uiview

  • iOS에서 크기 재조절, 회전 혹은 뷰의 움직임에 대한 뷰의 transform 속성을 사용할 수 있습니다. 그러나 이 변형들은 어떤 방식에서라도 오토 레이아웃의 계산에 영향을 미치지 않습니다. 오토 레이아웃은 변형되지 않은 프레임을 기준으로 뷰의 정렬 사각형을 계산합니다.

  • 뷰는 bounds 바깥에 컨텐트를 표시할 수 있습니다. 대부분 뷰는 bounds에서 뷰가 갖는 컨텐트를 적절하게 다루고 제한합니다. 그러나 성능상의 이유로 그래픽 엔진에 의해 강제되지는 않습니다. 이는 뷰(그리고 특히 커스텀으로 그려진 뷰)가 프레임에 비해 다른 크기로 그려진다는 것을 의미합니다.
    이 버그들은 뷰의 clipsToBounds 속성을 YES로 설정하거나 뷰 프레임의 크기를 검증하는 것을 통해 확인할 수 있습니다.

  • NSLayoutAttributeBaseline, NSLayoutAttributeFirstBaseline, NSLayoutAttributeLastBaseline 특성은 모든 뷰가 내재된 컨텐트 높이로 표시될 때에만 정확하게 텍스트를 정렬합니다. 만약 뷰의 하나가 수직으로 확장되거나 축소된다면, 텍스트는 옳지 않은 위치에 나타날 것입니다.

  • 제약 속성들은 전체 뷰 계층구조에 걸쳐 글로벌 속성처럼 행동합니다. 뷰를 스택뷰, 레이아웃 가이드, 더미뷰 내부에 그루핑하는 것을 통해 레이아웃을 간단하게 만들 수 있습니다. 그러나 이 접근법은 포함된 뷰들의 속성들을 캡슐화하지 않습니다. 오토 레이아웃은 그룹 밖의 우선순위를 그룹 내부 우선순위들과 비교합니다(혹은 다른 그룹에 속한 우선순위들).

  • aspect ratio 제약은 수평과 수직 제약들이 상호작용할 수 있도록 해줍니다. 수평과 수직 레이아웃은 각각 계산됩니다. 그러나 뷰의 높를 넓이에 상대적인 것으로 제약을 둔다면, 수직과 수평 제약을 연결해서 생성한 것입니다. 둘은 서로에게 영향을 미치기도 하고 충돌이 발생하기도 합니다. 이와 같은 상호작용은 레이아웃의 복잡성을 굉장히 증가시키고, 레이아웃의 서로 관련이 없는 부분들 사이에서 예쌍치 못한 충돌들을 발생시킬 수 있습니다.

0개의 댓글