[번역] Debugging Tricks and Tips - Debugging Auto Layout

작성일 :

개요


해당 문서는 학습 목적으로 Apple 공식 문서인 🔗 Auto Layout Guide을 번역한 글입니다. 다소 오역이 있을 수 있어 잘못된 내용이 있을 수 있습니다. 문제가 되거나 오류가 있다면 댓글 부탁드립니다.

Debugging Tricks and Tips


이번 챕터에서는 레이아웃에 대한 정보를 수집하고 구성하는 기술과 발생할 수 있는 몇 가지 놀라운 동작에 대해 설명합니다. 모든 레이아웃에서 이러한 기술을 사용할 필요는 없지만 가장 어려운 문제를 해결하는 데 도움이 될 수 있습니다.

Understanding the Logs


만족할 수 없는 레이아웃이 있거나 ConstraintsAffectingLayoutForAxis: 또는 constraintsAffectingLayoutForOrientation: 디버깅 방법을 사용하여 제약 조건을 명시적으로 기록했기 때문에 뷰에 대한 정보를 콘솔에 인쇄할 수 있습니다.

어느 쪽이든 이 로그에서 많은 유용한 정보를 찾을 수 있습니다. 만족할 수 없는 레이아웃 오류의 샘플 출력은 다음과 같습니다.

shell
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.

이 오류 메시지는 5개의 충돌하는 구속조건을 보여줍니다. 이러한 모든 제약 조건이 동시에 참일 수는 없습니다. 하나를 제거하거나 선택적 제약 조건으로 변환해야 합니다.

다행스럽게도 뷰 계층 구조는 비교적 간단합니다. 레이블과 텍스트 필드를 포함하는 슈퍼뷰가 있습니다. 충돌하는 제약 조건은 다음 관계를 설정합니다.

  1. 레이블의 너비는 400포인트보다 크거나 같습니다.
  2. 레이블의 leading edge는 수퍼뷰의 leading margin과 동일합니다.
  3. 레이블과 텍스트 필드 사이에는 8포인트 간격이 있습니다.
  4. 텍스트 필드의 trailing edge는 수퍼뷰의 trailing margin과 같습니다.
  5. 수퍼뷰의 너비는 320포인트로 설정됩니다.

시스템은 레이블의 너비 조건을 깨서 복구를 시도합니다.

NOTE

제약 조건은 Visual Format Language를 사용하여 콘솔에 기록됩니다. Visual Format Language를 사용하여 고유한 제약 조건을 만든 적이 없더라도 Auto Layout 문제를 효과적으로 디버깅하려면 Visual Format Language를 읽고 이해할 수 있어야 합니다. 자세한 내용은 🔗 Visual Format Language를 참조하세요.

이러한 제약 조건 중 마지막 제약 조건은 시스템에서 생성한 것입니다. 변경할 수 없습니다. 또한 첫 번째 제약 조건과 명백한 충돌이 발생합니다. 슈퍼뷰의 너비가 320포인트에 불과한 경우 400포인트 너비의 레이블을 가질 수 없습니다. 다행히 첫 번째 제약 조건을 제거할 필요는 없습니다. 우선순위를 999로 낮추면 시스템은 여전히 선택한 너비를 제공하려고 시도합니다. 다른 제약 조건을 여전히 만족시키면서 가능한 한 가깝게 제공합니다.

뷰의 autoresizing mask를 기반으로 하는 제약 조건(예: 🔗 translatesAutoresizingMaskIntoConstraints가 YES일 때 생성되는 제약 조건)에는 마스크에 대한 추가 정보가 있습니다. 제약 조건의 주소 뒤에 로그 문자열에는 h= 뒤에 세 문자가 표시되고 v= 뒤에 세 문자가 표시됩니다. A -(하이픈) 문자는 고정 값을 나타내고 &(앰퍼샌드)는 가변 값을 나타냅니다. 가로 마스크(h=)의 경우 세 문자는 왼쪽 여백, 너비 및 오른쪽 여백을 나타냅니다. 세로 마스크(v=)의 경우 위쪽 여백, 높이 및 아래쪽 여백을 나타냅니다.

예를 들어 다음 로그 메시지를 고려하십시오.

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

이 메시지는 다음 부분으로 구성됩니다.

  • NSAutoresizingMaskLayoutConstraint:0x7ff28252e480: 제약 조건의 클래스 및 주소. 이 예제에서 클래스는 보기의 자동 크기 조정 마스크를 기반으로 한다고 알려줍니다.
  • h=--& v=—&: 뷰의 autoresizing mask. 이것이 기본 마스크입니다. 가로로 고정된 왼쪽 여백, 고정된 너비 및 유연한 오른쪽 여백이 있습니다. 세로로 고정된 위쪽 여백, 고정된 높이 및 유연한 아래쪽 여백이 있습니다. 즉, 슈퍼뷰의 크기가 변경될 때 뷰의 왼쪽 상단 모서리와 크기는 일정하게 유지됩니다.
  • H:[UIView:0x7ff282617cc0(50)]: 제약 조건의 시각적 형식 언어 설명입니다. 이 예에서는 너비가 50포인트인 단일 보기를 정의합니다. 이 설명에는 제약 조건의 영향을 받는 뷰의 클래스와 주소도 포함됩니다.

Adding Identifiers to the Logs


이전 예제는 상대적으로 이해하기 쉬웠지만 더 긴 제약 조건 목록은 빠르게 따라가기가 어려워집니다. 모든 뷰 및 제약 조건에 의미 있는 식별자를 제공하여 로그를 더 쉽게 읽을 수 있습니다.

보기에 명백한 텍스트 구성 요소가 있는 경우 Xcode는 이를 식별자로 사용합니다. 예를 들어 Xcode는 레이블의 텍스트, 버튼의 제목 또는 텍스트 필드의 자리 표시자를 사용하여 이러한 보기를 식별합니다. 그렇지 않으면 Identity inspector에서 보기의 Xcode 특정 레이블을 설정합니다. Interface Builder는 인터페이스 전체에서 이러한 식별자를 사용합니다. 이들 중 다수는 콘솔 로그에도 표시됩니다.

제약 조건의 경우 프로그래밍 방식으로 또는 속성 관리자를 사용하여 해당 식별자 속성을 설정합니다. 자동 레이아웃은 콘솔에 정보를 인쇄할 때 이러한 식별자를 사용합니다.

예를 들어, 다음은 식별자가 설정된 동일한 만족할 수 없는 제약 조건 오류입니다.

shell
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.

보시다시피 이러한 식별자를 사용하면 로그에서 제약 조건을 빠르고 쉽게 인식할 수 있습니다.

Visualizing Views and Constraints


Xcode는 뷰 계층 구조의 뷰와 제약 조건을 시각화하는 데 도움이 되는 도구를 제공합니다.

시뮬레이터에서 뷰를 보려면 다음을 수행하십시오:

  1. 시뮬레이터에서 앱을 실행합니다.
  2. Xcode로 다시 전환합니다.
  3. Debug > View Debugging > Show Alignment Rectangles를 선택합니다. 이 설정은 뷰의 가장자리를 설명합니다.
image02

정렬 사각형은 오토 레이아웃에서 사용되는 가장자리입니다. 이 옵션을 켜면 예기치 않게 크기가 조정된 정렬 사각형을 빠르게 찾을 수 있습니다.

image04

더 많은 정보가 필요하면 Xcode 디버그 표시줄에서 Debug View Hierarchy 버튼(위 아이콘 모양)을 클릭하십시오. 그런 다음 Xcode는 대화형 뷰 디버거를 표시하여 뷰 계층 구조를 탐색하고 상호 작용할 수 있는 다양한 도구를 제공합니다. 자동 레이아웃 문제를 디버깅할 때 "Show clipped content" 및 "Show constraints" 옵션이 특히 유용합니다.

image03

"Show clipped content" 옵션을 활성화하면 실수로 화면 밖에 배치되었을 수 있는 뷰의 위치가 표시됩니다. "Show constraints" 옵션을 활성화하면 현재 선택된 보기에 영향을 미치는 모든 제약이 표시됩니다. 두 가지 옵션 모두 이상하게 작동하기 시작할 때 빠른 온전성 검사를 제공합니다.

자세한 내용은 🔗 Debug Area Help을 참조하세요.

Understanding Edge Cases


다음은 오토 레이아웃이 예기치 않은 방식으로 작동하도록 할 수 있는 몇 가지 극단적인 경우입니다.

  • 오토 레이아웃은 프레임이 아닌 정렬 사각형을 기준으로 보기를 배치합니다. 대부분의 경우 이러한 값은 동일합니다. 그러나 일부 보기는 레이아웃 계산에서 보기의 일부(예: 배지)를 제외하도록 사용자 지정 정렬 사각형을 설정할 수 있습니다. 자세한 내용은 *🔗 UIView Class Reference*의 오토 레이아웃으로 보기 정렬을 참조하십시오.
  • iOS에서는 뷰의 🔗 transform 속성을 사용하여 뷰의 크기를 조정하거나 회전하거나 이동할 수 있습니다. 그러나 이러한 변환은 Auto Layout의 계산에 어떤 식으로든 영향을 미치지 않습니다. 자동 레이아웃은 변환되지 않은 프레임을 기반으로 보기의 정렬 사각형을 계산합니다.
  • 뷰는 bounds 밖에 있는 콘텐츠를 표시할 수 있습니다. 대부분의 경우 뷰는 적절하게 동작하고 콘텐츠를 경계로 제한합니다. 그러나 성능상의 이유로 이것은 그래픽 엔진에 의해 시행되지 않습니다. 이는 뷰(특히 사용자 지정 드로잉이 있는 뷰)가 해당 프레임과 다른 크기로 그려질 수 있음을 의미합니다. 보기의 🔗 clipsToBounds 속성을 YES로 설정하거나 뷰의 프레임 크기를 확인하여 이러한 버그를 식별할 수 있습니다.
  • 🔗 NSLayoutAttributeBaseline, 🔗 NSLayoutAttributeFirstBaseline🔗 NSLayoutAttributeLastBaseline 속성은 모든 뷰가 고유 콘텐츠 높이에 표시될 때만 텍스트를 올바르게 정렬합니다. 뷰 중 하나가 수직으로 늘어나거나 줄어들면 해당 텍스트가 잘못된 위치에 나타날 수 있습니다.
  • 제약 조건 우선 순위는 전체 뷰 계층 구조에서 전역 속성으로 작동합니다. 스택 뷰, 레이아웃 가이드 또는 더미 뷰 내에서 뷰를 그룹화하여 레이아웃을 단순화할 수 있습니다. 그러나 이 접근 방식은 포함된 뷰의 우선 순위를 캡슐화하지 않습니다. 오토 레이아웃은 그룹 내부의 우선 순위를 그룹 외부의 우선 순위(또는 다른 그룹 내부의 우선 순위까지)와 계속 비교합니다.
  • 종횡비 제약 조건을 사용하면 수평 및 수직 제약 조건이 상호 작용할 수 있습니다. 일반적으로 가로 및 세로 레이아웃은 별도로 계산됩니다. 그러나 뷰의 너비를 기준으로 높이를 제한하면 세로 및 가로 제한 사이에 연결이 생성됩니다. 이제 서로 영향을 미치고 충돌할 수 있습니다. 이러한 상호 작용은 레이아웃의 복잡성을 크게 증가시키고 레이아웃의 관련 없는 부분 간에 예기치 않은 충돌을 일으킬 수 있습니다.