반응형

Ray로 부터 알림: 축하한다. 여러분들이 해냈다. iOS 6 Feast 단어를 널리 알리는 것을 도와줘서, 첫번째 iOS 6 튜토리얼을 미리 공개하게 되었다!

이 튜토리얼은 새로운 책인 iOS 6 튜토리얼의 하나의 챕터의 축약된 버젼이다. Matthijs Hollemans가 작성하였다. 그는 iOS Apprentice Series를 작성한 사람이다. 재미있게 즐겨라!

이 포스트는 iOS 튜토리얼 팀 맴버이면서 iOS 개발자와 디자이너의 경험이 있는 Matthijs Hollemans가 작성하였다.

이 튜토리얼의 파트 1에서 이전 방식의 “스트러츠-스프링” 모델으로 유저 인터페이스로 만들면 모든 레이아웃 문제를 쉽게 해결 할 수 없는 것을 보았다. iOS 6의 강력한 기술인 오토 레이아웃은 해결책을 제시한다.

이 튜토리얼 시리즈의 마지막은 두번째 파트에서 계속해서 제약조건의 모든 것과 어떻게 그것을 적용 할지에 대해 배울 것이다.

볼트체의 사용자 제약조건

캔버스에서 T자 모양의 바의 몇개는 다른 것보다 굵은 것을 아마도 알았을 것이다. 굵은 것들은 사용자 제약조건이라고 불린다. 그리고 얇은거와는 달리 그것을 삭제 할 수 있다. 그러나 사용자 제약조건이 삭제되면 인터페이스 빌더는 지울수 없는 제약조건을 그 자리에 삽입한다. 왜 그런지 잠시후에 볼 수 있다.

Document Outline에서 사용자 제약조건은 파란색 아이콘을 가진다.

도큐먼트 아웃라인에서 사용자 제약조건

Vertical Space (40) 제약조건을 선택하고 키보드에서 Delete 키를 누른다. 두 버튼 사이의 T자-바가 사라진다. 그리고 새로운 Vertical Space 제약조건으로 대체된다.

사용자 제약조건을 삭제 후

새로운 제약조건은 자주빛 아이콘을 가지고 굵은 선을 가지지 않는다. 이것은 지울 수 없다는 것을 의미한다. 두 개의 버튼은 더 이상 수직적으로 연결되지 않는다. 비록 두 개의 버튼은 Leading Alignment 제약조건 때문에 여전히 왼쪽 정렬된 상태이다.

왜 이런 일이 일어 나는가? 왜 인터페이스 빌더는 새로운 Vertical Constraint를 버튼에 덛붙이는가? 겨우 이와 같은 제약조건을 삭제 하라고 해서? 답은 이것이다.

각각의 뷰는 거기에 그것의 위치와 크기를 결정하기 위해 충분한 제약조건이 있어야 한다.

저것은 매우 오토 레이아웃을 사용 할 때 기억해야 할 가장 중요한 법칙이다. 제약조건이 충분하지 않다면, 오토레이 아웃은 뷰의 위치나 크기를 계산하지 못 할 것이다. 이러한 레이아웃은 무효로 간주된다. 이런 무효한 레이아웃의 예를 나중에 볼 수 있다.

인터페이스 빌더는 무효한 레이아웃을 만드는 것을 방지하기 위해 노력을 한다. 버튼이 자신의 크기가 얼마나 되야 적당한지를 자신의 텍스트, 배경 이미지 등을 기반으로 알고 있기 때문에 두 개의 버튼의 크기는 알려져 있다. 그러면 문제가 없다. 또한 위쪽 버튼의 X값은 아래쪽 버튼에 그것의 왼쪽 가장자리가 정렬되어 있고 아래쪽 버튼은 항상 수평적으로 가운데에 있기 때문에 알려져 있다. 오직 모르는 것은 Y값이다.

이전에는 Vertical Space로 두 개의 버튼이 연결되어 있었다. 그것은 위쪽 버튼의 Y 위치를 결정하는데 충분하다. 그러나 Vertical Space를 삭제하면, 위쪽 버튼은 뷰의 수직적으로 아무것도 고정하지 않는다. 오토 레이아웃은 Y 좌표가 어디에 있어야할지를 결정할 수 있는 방법이 없기 때문에 거기에 고정시킬수 없다.

이와 같은 문제를 방지 하려면 인터페이스 빌더는 아래쪽 가장자리의 가장 가까운 곳에 버튼을 “고정” 시켜야 한다.

Pin all the buttons

앱을 실행하고 가로 방향으로 회전시키면, 우습기는 하지만 여전히 작동하는 것 같다. 화면은 이전에 했던 것처럼 동일하게 보인다. 그것은 사실이나, 디자인은 근본적으로 다르다. 아래 버튼은 움직이나 위 버튼은 여전히 움직이지 않는 것을 의미한다. 두 해결책은 괜찮다. 앱에 무엇을 하고 싶은지에 따라 달라진다. 예를 들면 두 개의 버튼사이에 수직적인 연결을 가지고 한다.

이 설명을 하기 위해 아래쪽 버튼과 화면의 가장자리의 사이의 Vertical Space 제약조건을 선택한다. Attribute 인스팩터로 이동한다. 그것의 Constant는 현재 “Auto”로 읽는다. 그리고 Standard는 표준 여백 공간이므로 체크되어 있다. 40으로 변경한다.

버튼이 연결되지 않았기 때문에, 오직 아래쪽 버튼이 위로 이동한다. 위쪽 버튼은 머물러 있다.

Only bottom button moves up

제약조건의 Constant 값을 변경하면 그것이 “user” 제약조건으로 볼드화되여 변경 되는 것을 확인할 수 있다.

바늘과 핀

두 개의 버튼을 다시 연결해 보자. 캔버스에 버튼을 드래그해서 제약조건을 만들 뿐만 아니라 그것들을 나중에 만들 수도 있다. Cmd키를 누르고 두 개의 버튼을 클릭해서 선택한다. Editor 메뉴에서 PinVertical Spacing을 선택한다.

이 제약조건을 만들기 위해 하단의 오른쪽 구석의 작은 페널을 사용 할 수도 있다.

핀 메뉴 단축키

다음과 같이 메뉴가 팝업된다.

핀 메뉴

선택한 방법에 상관없이 두 개의 버튼 사이에 새로운 제약조건 추가한다.

수직 공간에 핀

20 포인트를 가진 새로운 Vertical Space 제약조건이다. 이 연결을 만든 때에 두 버튼의 사이는 20포인트의 거리를 두고 있기 때문이다.

위쪽 버튼부터 아래쪽 가장자리의 이전 Vertical Space는 아직 있다는 것을 확인한다. Vertical Space (104) 이 제약조건은 더 이상 필요 없으므로 삭제한다.

이전에 파란색 제약조건을 삭제할때 보라색이 대체 되었다. 나머지 제약조건은 모든 뷰를 배치하기 충분하기 때문에 지금은 그런게 없다.

다음의 제약조건을 가지고 있어야 한다.

복구된 제약조건

캔버스에서 클릭하여 아래 Vertical Space를 선택한다. 그리고 40에서 표준으로 그것의 Constant를 변경한다. 다시 버튼들이 연결되어 있기 때문에 아래 버튼을 아래쪽으로 이동하면 안되고 위 버튼도 마찬가지다.

작은 런타임 여행

이제 기본적인 것을 살펴 봤다. 가이드를 사용하여 콘트롤을 어떻게 배치하는지, 다른 콘트롤러 사이를 어떻게 정렬하는지 그리고 콘트롤 사이에 공간을 어떻게 넣는지를 알 것이다. 이 튜토리얼 과정을 통해서 Align과 Pin 메뉴로 다른 옵션을 사용 할 것이다.

인터페이스 빌더에서 다룬 것은 잘 작동하고 좋아 보인다. 그러나 실행시에는 이 작업이 어떤지 살펴보자. ViewController.m에 다음의 메서드를 추가한다.

- (IBAction)buttonTapped:(UIButton *)sender
{
   if ([[sender titleForState:UIControlStateNormal] isEqualToString:@"X"])
       [sender setTitle:@"A very long title for this button"
               forState:UIControlStateNormal];
   else
       [sender setTitle:@"X" forState:UIControlStateNormal];
}

버튼의 긴 제목과 짧은 제목을 전환한다. 인터페이스 빌더에서 두 개의 버튼과 엑션 메서드를 연결한다. 각각의 버튼을 콘트롤을 누르고 File’s Owner로 드래그 한다. 팝업에서 buttonTapped:을 선택한다.

앱을 실행하고 작동을 확인하기 버튼을 탭한다. 가로방향과 세로방향에서 테스트를 수행한다.

길고 짧은 타이틀

긴 타이틀과 짧은 타이틀에 상관없이 레이아웃은 주어진 제약조건을 항상 만족한다.

  • 아래 버튼은 윈도우에서 수평적으로 항상 가운데 정렬이다.
  • 아래 버튼은 윈도우 아래로 부터 20포인트에 위치한다.
  • 위 버튼은 아래 버튼에 왼쪽 정렬을 한다.

그것은 유저 인터페이스의 전체 사양이다.

재미를 위해 인터페이스 빌더에서 두 개의 버튼을 선택하고 Align 메뉴에서 Right Edges 선택한다. 앱을 다시 실행하고 달라진 것을 확인한다.

반복하지만 지금은 AlignHorizontal Centers를 선택한다. 그것은 아래 버튼을 고려해서 위 버튼을 항상 가운데 위치한다. 앱을 실행하고 버튼을 탭할때 버튼이 어떻게 반응하는지 봐라.

넓이 고정

핀(Pin)메뉴는 Widths Equally 옵션을 가진다. 두 개의 뷰에 이 제약조건을 설정하면, 오토 레이아웃은 두 개의 뷰를 큰 뷰를 기준으로 항상 같은 넓이로 만든다. 잠깐 가지고 놀아보자.

두 개의 버튼을 선택하고 PinWidths Equally을 선택한다. 두 개의 버튼에 새로운 제약조건을 추가 했다.

Buttons widths equally

메모: 두 개의 버튼 중 하나와 슈퍼뷰 사이에서 여분의 불필요한(unintended) 제약조건을 얻을려면, 두 개의 버튼을 선택하고 AlignHorizontal Center를 다시 선택하라.

이 튜토리얼의 첫번째 파트에서 이런 제약조건의 유형을 이전에 보았다. 이것은 평범한 T자 막대기로 보이지만 등호(=)를 가진 원이 중간에 있다.

Document Outline에서 하나의 Equal Widths 제약조건으로 나타난다.

Document Outline에서 Equal widths

하나의 버튼의 레이블 텍스트를 변경하면 다른 하나의 버튼의 크기도 변경 될 것이다.

아래 버튼의 레이블을 가장 작은 크기로 만들기 위해서 “X”로 변경한다. 위쪽 버튼이 더이상 텍스트의 크기에 맞지 않는 것을 볼 수 있다.

위 버튼의 텍스트는 더이상 맞지 않는다

인터페이스 빌더는 두 개의 버튼에 사용하는수 있는 크기를 어떻게 알 수 있을까? 주의깊게 보면 Width 제약조건이 텍스트가 잘린 버튼에 추가 된 것을 볼 수 있다.

Width constraint on truncated button in document outline

Width constraint on truncated button

인터페이스 빌더는 Equal Widths 제약조건을 따르기 위해 이상적인 조건 보다 강제적으로 버튼을 작게 한다.

분명히 이것은 원하는것이 아니다. 그래서 위쪽 버튼을 선택하고 Editor에서 Size to Fit Content를 선택한다. 또는 Cmd =를 누른다. 다시 텍스트의 크기는 버튼에 딱맞게 되었거나 버튼이 텍스트 주변에 딱 맞게 되었다. 그리고 Width 제약조건은 사라졌다.

앱을 실행하고 버튼을 탭한다. 가장 긴 레이블에 관계 없이 버튼은 항상 같은 넓이를 가진다.

Buttons equal widths in app

물론 두 개의 레이블이 매우 짧을 때도 두 개의 버튼은 동일하게 줄어든다. 방지하는 제약조건이 있지 않지만, 버튼은 더 크지도, 더 작지도 않게 컨텐츠의 크기에 맞게 크기가 꼭 맞게 된다. 무엇이라고 불렸는가? 고유 컨텐츠 크기이다.

고유한 콘텐츠 크기

오토 레이아웃 전에는 인터페이스 빌더에서 그것의 프레임 또는 경계 속성을 설정할 때, 그것들의 크기를 조정할 때는 버튼이나 다른 컨트롤러에 그것들의 크기를 항상 설정했다. 그러나 대부분의 콘트롤러는 콘텐츠를 기반으로 필요하는 공간의 양을 완벽하게 결정했다.

레이블은 텍스트의 폰트 크기와 텍스트의 길이를 알고 있기 때문에 넓이와 높이를 알 수 있다. 배경이미지와 둥근모서리를 위한 패딩을 가진 텍스트 결합한 버튼도 마찬가지다.

비록 일부는 높이는 정해져있고 너비는 알수 없다고 하지만, 세그먼트 콘트롤, 프로그레스 바, 그리고 대부분의 다른 컨트롤에도 동일하게 적용된다.

이것은 고유한 컨텐츠 크기로 알려져 있다. 그리고 이것은 오토 레이아웃의 중요한 컨셉이다. 버튼에서 이런 액션을 보았을 것이다. 오토 레이아웃은 컨트롤러에 필요한 크기를 요청하고 정보에 따라 화면을 배치 한다.

컨트롤에 명시적인 Width 또는 Height 제약조건을 설정하여 이 문제를 방지 할 수 있다. 손으로 컨트롤의 크기를 조정할 경우, 인터페이스 빌더는 이러한 명시적 제약조건을 설정한다. Size to Fit Content 명령어로 모든 고정된 Width 또는 Height 제약조건을 제거 할 수 있고, 콘트롤은 다시 고유 컨텐츠 크기를 결정 할 수 있다.

보통 고유 컨텐츠 크기를 사용을 원하지만, 원하지 않는 몇 가지 경우가 있다. 이미지가 화면보다 훨씬 큰 경우 UIImageView에 이미지를 설정할 때 어떤 일이 벌어지는 지 상상하라. 이미지의 크기로 뷰의 크기를 조정하는 것을 원하지 않으면, 이미지 뷰에 고정된 너비와 높이를 주고 컨텐츠를 확장한다.

그래서 버튼중 하나가 고정 Width 제약조건을 가졌을때 무엇이 발생하나? 버튼은 자신의 크기를 계산하지만 고정 넓이를 제공하여 오버라이드 할 수 있다. 위 버튼을 선택하고 PinWidth를 메뉴에서 선택한다. T자 막대기를 버튼 밑에 추가한다.

고정 넓이 제약조건을 가진 버튼

버튼의 슈퍼뷰가 아니라 버튼 자체에 이러한 제약조건 종류가 적용되기 때문에, Document Outline의 버튼 오브젝트 아래에 표시된다. 이 경우에는 버튼이 73포인트의 고정 넓이를 가졌다.

앱을 실행하고 버튼을 탭한다. 무엇이 일어나는가? 버튼의 텍스트는 변경되지만 충분한 공간이 없어서 텍스트가 잘린다.

잘린 버튼 텍스트

위쪽 버튼이 고정 넓이 제약조건을 가졌고 버튼은 동일한 크기를 요구하고 있기때문에 그것들은 절대 줄어들거나 늘어날수 없다.

메모: 버튼에 Width 제약조건을 아마도 설정하지 않을 수도 있다. 버튼의 고유 크기를 사용하는 것이 가장 좋다. 그러나 인터페이스 빌더가 하지 않고 컨트롤러의 싸이즈를 변경을 예상하는 곳의 레이아웃 문제를 실행하면, 인터페이스 빌더가 고정된 Width 제약조건을 모르게 설정하지 않도록 두 번 체크 하라.

뷰의 고정(Pin)과 정렬을 지겨워질때까지 가지고 놀아봐라. 처음부터 바로 알 수 없으니 점점 자유롭게 다룰 수 있을 것이다. 오토 레이아웃은 모든 뷰의 위치와 크기를 결정할 수 있도록 충분한 제약조건이 있다는 것만 명심하면 된다.

Got enough constraints

예제 갤러리

제약조건이란 무엇인지와 다른 뷰 사이의 강제적인 관계에 의해 레이아웃이 만들어지는 방법의 아이디어를 가지고 있어야 한다. 다음 섹션에서는 오토 레이아웃을 사용하는 방법과 실제 시나리오를 만족하는 레이아웃을 만드는 제약조건을 살펴 볼 것이다.

좋아하는 프로그래머의 갤러리를 가진 앱을 만들기를 가장하자. 가로 방향과 세로 방향에서는 다음과 같이 보인다.

갤러리 앱

화면은 4개의 똑같은 크기로 나눠져 있다. 각각은 이미지 뷰와 레이블을 가지고 있다. 어떻게 접근할까?

기본 앱을 설정으로 시작해보자. 기존의 “Constrants” 앱을 버튼을 삭제하고 뷰를 재사용해서 사용 할 수 있다.

또는 Single View Application 템플릿을 사용해서 새로운 프로젝트를 만들수 있다. 그리고 이름을 “Gallery”와 같이 마음에 드는 이름을 부여한다. nib만 사용 할 것이고 스토리보드 옵션을 해제한다.

ViewController.xib을 연다. Object Library에서 기본 뷰 오브젝트를 캔버스로 드래그 한다. 160 x 230 포인트의 크기로 조절한다. 그리고 배경색을 하얀색을 제외하고 바꾼다. 여기서는 녹색으로 했다.

오토레이 아웃 뷰

이 뷰는 위치를 유지하기 위한 4개의 제약조건이 있다. 버튼이나 레이블과는 달리 기본 UIView는 고유 콘텐츠 크기를 가지고 있지 않는다. 각각의 뷰의 위치와 크기를 결정하는 충분한 제약조건이 항상 있다. 그래서 이 뷰는 필요로 하는 크기가 무엇인지 제약조건에 알려주어야 한다.

이런 크기 제약조건이 어디에 있는지 궁금할 수 있다. 이 경우에는 뷰의 크기는 슈퍼뷰의 크기에 의해 암시된다. 이 레이아웃의 제약조건은 두 개의 Horizontal Space와 두 개의 Vertical Space 이다. 그리고 모두 고정 넓이를 가진다. Document Outline에서 이와 같이 볼 수 있다.

Document Outline에서 UIView의 제약조건

녹색 뷰의 넓이는 “슈퍼뷰의 넓이 빼기 (109 + 51)” 공식에의해 계산된다. 높이는 “슈퍼뷰의 높이 빼기 (153 + 77)” 공식에 의해 계산된다. 공간 제약조건은 고정된다. 그래서 뷰는 크기조정은 되나 선택은 안된다. 앱을 화면전환 했을 때, 슈퍼뷰의 크기는 320×640에서 480×300으로 변경된다. 이 새로운 너비와 높이가 공식으로 변환되어 녹색 뷰의 새로운 크기를 얻을 수 있다.

앱을 실행하고 가로화면으로 회전할 때 다음과 같이 볼 수 있다. 그러나 인터페이스 빌더에서 직접적으로 그것을 시뮬레이션 할 수 있다.

nib에서 맨 위쪽 뷰를 선택하고 Attributes 인스펙터로 이동한다. Simulated Metrics 섹션 밑에 Orientation을 Landscape로 변경한다.

Simulated metrics landscape

이렇게 하면 가로 방향에서 nib의 레이아웃을 어떻게 보이는지 간단하게 미리보기를 할 수 있다. 녹색 뷰는 Horizontal과 Vertical Space 제약조건에 만족하기 위해 크기가 조정된다.

다시 세로 화면으로 전환 한다.

메모: nib으로 평범한 UIView를 드롭하는 두가지 메인 이유가 있다. 1) nib의 컨텐츠를 정리하는 것을 돕기 위한 다른 뷰의 컨테이너로 사용 한다. 2) 커스텀 뷰 또는 컨트롤러의 플레이스 홀더로 사용하고 Class 속성을 UIView 또는 UIControl 서브클래스의 이름으로 설정 할 수 있다.

기기 회전을 할때 항상 UIView의 크기 조절을 원하지 않을 수 있다. 그래서 뷰에 고정된 너비 그리고/또는 높이를 주기위해 제약조건을 사용 할 수 있다. 녹색 뷰를 선택하고 Pin 메뉴에서 Width를 선택한다. 뷰를 다시 선택하고 PinHeight를 선택한다.

뷰에 두 개의 제약조건(160 포인트의 Width 제약조건과 230 포인트의 Height 제약조건)을 추가 했다.

UIView의 Width와Height 제약조건

Width와 Height를 이 뷰에 적용하기 때문에 Documents Outline에 View아래에 그것들이 위치한다. 예를 들면 Horizontal과 Vertical Space 제약조건은 녹색 뷰와 그것의 슈퍼뷰인 회색 뷰 처럼, 일반적이지 않게 제약조건은 두 개의 다른 뷰를 표현하지만 뷰와 자체 사이의 관계로 Width와 Height 제약조건을 고려 할 수 있다.

앱을 실행한다. 세로 방향에서도 보기 좋다. 다시 가로 방향으로 회전한다. 원하는 것 처럼 되지 않았을 뿐만 아니라 Xcode 디버그 창에 심각한 오류 메세지가 나타났다.

Gallery[68932:11303] 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:0x754dac0 V:[UIView:0x754e510(230)]>",
   "<NSLayoutConstraint:0x754eac0 V:|-(77)-[UIView:0x754e510]   (Names: '|':UIView:0x754e3a0 )>",
   "<NSLayoutConstraint:0x754ea40 V:[UIView:0x754e510]-(153)-|   (Names: '|':UIView:0x754e3a0 )>",
   "<NSAutoresizingMaskLayoutConstraint:0x7558cd0 h=-&- v=-&- UIView:0x754e3a0.width == UIWindow:0x71156e0.width - 20>",
   "<NSAutoresizingMaskLayoutConstraint:0x74128b0 h=--- v=--- H:[UIWindow:0x71156e0(320)]>"
)
 
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x754dac0 V:[UIView:0x754e510(230)]>
 
Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

충분한 제약조건이 있어야 오토 레이아웃이 모든 뷰의 위치와 크기를 계산할 수 있다고 말한 것을 기억하는가? 이 예제에는 너무 많은 제약 조건이 있다. “Unable to simultaneously satisfy constraints” 오류를 발생 할 때마다, 어디선가 제약조건이 충돌이 난다는 것을 의미한다.

다시 제약조건을 살펴 보자.

충돌난 제약조건

녹색 뷰에는 6개의 제약조건이 설정되어 있다. 이전에 봤듯이 4개의 Space 제약조건과 그것에 설정한 2개의 새로운 Width와 Height 제약조건이다. 그래서 어디에서 충돌이 일어났나?

세로 방향 모드에서는 계산이 추가 되었기 때문에 문제가 되지 않는다. 슈퍼뷰의 너비는 320 포인트이다. Horizontal Space 제약조건의 너비와 뷰의 너비를 추가하는 경우 320으로 설정되어야 한다. 뷰를 배치한 방법은 실제로 51 + 160 + 109 = 320 이다. 마찬가지로 수직 제약 조건은 460까지 추가해야 한다.

그러나 기기를 가로 방향으로 회전했을 때, 윈도우는 490 포인트 너비를 가진다. 그 뜻은 51 + 160 + 109 + ? = 480 이다. 방정식과 오토 레이아웃이 그것들을 어디에 위치시킬지 모르는 것을 위해 필요한 160이 여분의 포인트가 있다. 수직축도 마찬가지다.

여기 충돌은 뷰의 너비가 고정되어 있으며 하나의 마진은 유동적이여야 한다, 또는 마진은 고정되어 있고 너비는 유동적이여야 한다는 것이다. 그래서 이런 제약조건중 하나가 실행되었다. 위의 예제에서는 가로와 세로 방향 모두에서 뷰가 같은 너비를 가지기를 원했다 그래서 Horizontal Space 진행됐다.

오른쪽의 Horizontal Space와 아래쪽의 Vertical Space를 제거한다. nib은 다음과 같을 것이다.

충돌 제약조건 수정

이제 뷰는 크기와 위치를 결정하는 더 많지도 적지도 않은 적당한 수의 제약조건을 가지고 있다. 앱을 실행하고 오류 메세지가 사라진 것과 화면 회전후 뷰가 같은 크기를 유지하는지 확인하라.

메모e: 인터페이스 빌더가 잘못된 레이아웃을 방지하기 위해 최선의 노력을 하지만, 그것은 기적을 수행 할 수는 없다. 적어도 오토 레이아웃은 뭔가 잘못되었을 때 자세한 오류를 뱉는다. iOS 6 튜토리얼의 “중금 오토 레이아웃”에서 이런 에러 메세지를 진단하고 오류 메세지를 분석하는 것을 배운다.

세로 방향 색칠하기

녹색 뷰에 두 개의 레이블을 드래그 한다. 녹색 뷰가 레이블의 슈퍼뷰이기 때문에 녹색 뷰에 가이드가 나타난다.

녹색 뷰에 레이블 드래기 하기

가이드에 대한 왼쪽 위의 구석에 레이블을 배치한다. 녹색 뷰의 왼쪽 위 구석에 레이블을 고정하는 두 개의 Space 제약조건을 추가한다.

레이블 제약조건

매인 뷰가 아니라, 녹색 뷰의 제약조건 섹션 밑에 두 개의 새로운 Horizontal과 Vertical Space 제약조건이 리스트되는 것을 확인할 수 있다.

녹색 뷰로 이동해 보자. 녹색 뷰와 그것의 슈퍼뷰의 사이의 유일한 제약조건의 변경을 볼 수 있다. 레이블은 항상 녹색 뷰와 상대적으로 같은 위치에 위치해야 한다.

레이블을 선택하고 아래 마진, 수평적으로 가운데에 위치시킨다. 그리고 새로운 이미지 뷰 오브젝트를 nib에 드래그 한다. 다음과 같은 레이아웃이 만들어 질 것이다.

Image view in gallery

이미지 뷰는 슈퍼뷰의 위, 왼쪽과 오른쪽 가장자리에 고정된다. 그러나 아래는 표준 간격으로 레이블의 상단과 연결되어 있다.

이 튜토리얼의 리소스를 다운로드 받고 압축을 푼다. Images 폴더를 프로젝트에 추가하라. Ray.png 파일을 이미지 뷰의 이미지로 설정한다. 이미지뷰의 모드를 Aspect Fit으로 변경하고 배경색으로 하얀색으로 설정한다. 레이블의 텍스트를 “Ray”로 변경한다.

레이아웃은 다음과 비슷할 것이다.

Ray의 갤러리

이제 인터페이스 빌더는 Height 제약조건을 배치한것을 확인 할 수 있다. 이미지 뷰에 이미지를 설정 한 순간에 일어났다.

높이가 고정된 레이블

인터페이스 빌더는 모호한 레이아웃으로 알려진 것을 방지하기 위해 시도 한다. 이미지 뷰나 레이블이 고정된 높이를 가지고 있다면, 오토 레이아웃은 녹색 뷰의 높이를 변경해야 할 각각의 크기를 조정하는 방법을 알지 못한다. (인터페이스 빌더는 이제 녹색의 뷰가 실제로 거기에 설정할 고정된 Height 제약사항을 가지는 것을 무시하는 것 같다.)

앱의 그린뷰의 어떤 포인트가 100 포인트 커진다고 가정해보자. 오토 레이아웃은 새로운 100 포인트를 레이블과 이미지 뷰 중에 어디에 어떻게 배포를 해야할까? 레이블이 같은 크기로 있는 동안에 이미지 뷰가 100 포인트 커져야 할까? 아니면 이미지 뷰가 같이 크기로 있는 동안에 레이블이 100 포인트 켜져야 할까? 각각 50 포인트 또는 25/75, 40/60으로 나눠야 할까? 다른 가능한 방법으로 조합해야 할까?

오토 레이아웃은 추측할 수 없다. 그래서 인터페이스 빌더가 레이블에 고정된 높이를 줘서 이 문제를 “해결” 한다. 또한 이미지 뷰에 고정된 높이를 줄 수 있지만 레이블에 주는 것이 더 의미가 있다.

지금은 레이블에 Height 제약조건을 주도록 하자.

메모: 이 문제에 대한 적절한 해결방법은 레이블의 “Content Compression Resistance Priority”를 변경하는 것이다. 이것에 대해서 나중에 배울 것이다. 기다릴 수 없다면, 레이블의 Size 인스펙터로 이동해서 Vertical Content Compression Resistance Priority를 751로 설정해라. 레이블에 Height 제약조건은 이제 사라진다.

다른 헤드를 추가하기

메인 뷰의 왼쪽-위 구석으로 녹색 뷰를 옮긴다. 녹색 뷰는 부모 뷰에서 그것의 위치를 결정하는 Horizontal Space와 Vertical Space 제약조건을 가진다고 상기하자. 여전히 그것들을 가지고 있지만 지금은 값을 0으로 설정하자. 윈도우의 위와 왼쪽 가장자리에 하얀색 테투리를 가진 굵은 파란색 선으로 표시된다.

왼쪽-위 구석의 뷰

그래서 비록 뷰가 완벽하게 구석에 있더라도, 여전히 거기에 고정 시킬 제약조건이 필요하다. 0값을 가진 마진으로 생각하라.

녹색 뷰를 선택하고 Cmd-D를 눌러서 복사하라. 복사된 것을 오른쪽-위 구석으로 옮겨라.

오른쪽-위 구석의 녹색 뷰

2개 더 복사하고 이것들을 왼쪽-아래와 오른쪽-아래 구석에 각각 이동한다.

화면 디자인은 다음과 같이 변경한다.

갤러리 디자인

이것들은 잘생긴 프로그래머들이다! :-)

앱을 실행한다. 세로 방향에서는 괜찮게 보이지만 가로 방향에서는 그렇지 않다.

가로 방향에서 잘못된 갤러리

뭔가 잘못된 것이 분명하다. 고정된 너비와 높이를 뷰에 설정했다. 그래서 그것들은 슈퍼뷰의 크기에 관계 없이 항상 저런 크기를 가진다.

모든 4개의 뷰에서 Width (160)과 Height (230) 제약조건을 선택한다 그리고 그것들을 삭제한다. 앱을 다시 실행하면 다음처럼 보일 것이고 이것 또한 좋아 보이지는 않는다/

가로 방향에서 여전이 이상하게 보임

초반에 우리가 해결한 문제와 매우 비슷하게 보인다. 그래서 어떻게 해결했는지를 생각하면 뷰에 평등한 너비와 높이를 준 것을 기억 할 수 있다.

4개의 뷰를 선택하고 PinWidths Equally를 선택한다. 뷰를 다시 선택하고 PinHeights Equally를 선택하라.

앱을 다시 실행하고 기기를 회전하라. 흠… 여전히 전과 정확하게 똑같이 보인다. 왜?

스크린 샷을 보면 모든 뷰는 똑같은 높이를 가지고 있는 것을 볼 수 있다. 또한 똑같은 너비를 가지고 있다. (녹색과 갈색 뷰는 노란색과 파란색 뷰 때문에 부분적으로 보인다). 그래서 제약조건이 충족되고 있다. 이것은 원했던 너비와 높이가 아니다. 해결 방법을 얻기 위한 다른 제약조건이 있어야 한다.

이런 뷰의 제약조건을 보면, 그것들을 강제적으로 배치하게 하는 Horizontal과 Verticdal Space 제약조건을 가지고 있는 것을 볼 수 있다. 4내의 서브뷰가 아니라 메인 뷰의 제약조건 리스트를 보아라.

잘못된 Horizontal Space

더 나쁜것은 그런 제약조건을 삭제 할 수 없다. T자 막대기는 굵게되지 않고 제약조건은 파란색이 아니다. 그래서 인터페이스 빌더는 레이아웃 문제를 방지하기 위해서 거기에 그것을 넣었다.

오토 레이아웃은 4개의 뷰가 각각 어떻게 연결되어 있는지를 알 수가 없기 때문에, 4개의 뷰가 가져야 할 똑같은 크기는 그것들의 크기를 실제로 결정하기에 충분하지 않다. 그것들은 디자인에 나란히 표시되지만, 그것들 사이에 실제 제약조건은 없다. 오토 레이아웃은 “Ray”와 “Matthijs” 박스사이에서 윈도우 너비가 분할 할 필요가 있는지 알지 못한다.

오토 레이아웃은 스스로 알 수 없으면, 그것을 전달해야만 한다.

연결됨

Ray와 Matthijs 박스를 선택하고 PinHorizontal Spacing를 선택한다. 박스는 나란히 있기 때문에, 그것들 사이에 크기를 0으로 Horizontal Space 제약 조건 추가한다. 그리고 그것은 두 개의 뷰가 어떻게 연결되었는지 알기에 충분하다.

중요t: 인터페이스 빌더는 자동으로 슈퍼뷰와 노란색 박스 사이의 Horizontal Space를 제거하지 못한다. 그러나 그것을 사용자 제약조건(두꺼운 막대기)으로 만든다. 이제 이 공간을 삭제 할 수 있다. 만약 못한다면, 가로 방향으로 회전하는 동안에 “Unable to simultaneously satisfy constraints” 오류가 발생할 것이다.

앱을 실행하라. 다음과 같이 보일 것이다.

조금 괜찮아진 가로 방향

조금 나아 보인다. 4개의 박스는 동일한 너피를 가졌다. 그러나 높이는 여전히 잘못 되었다. 해결방법은 비슷하다. Ray와 Dennis Ritchie 박스 사이에 Vertical Space를 넣는다. 그리고 Dennis Ritchie와 윈도우 위 사이의 Vertical Space를 삭제한다.

앱을 다시 실행하라. 지금은 모든 것이 올바르게 되어 보인다.

올바른 가로 방향의 갤러리

이미지 뷰 아래의 “Dennis Ritchie” 레이블이 중앙에 있지 않다. 레이블의 텍스트를 입력 할 때 발생 했다. 레이블은 뷰의 초기에 중앙에 있지만, 인터페이스 빌더가 잘 알고나서 Horizontal Space로 중앙 제약조건을 교체했다. 이런 일이 생겼으면, 수정하기 위해 레이블을 선택하고 AlignHorizontal Center in Container을 선택하라.

이미지 뷰의 메모. 고정 크기를 주지 않았기 때문에 이미지 뷰는 늘어난다. 알고 있지 않았다면, 의도적인 한 부분이다. ☺ 이미지 뷰가 가로 방향에서 맞지 않을 것이다. 그러나 이미지 뷰가 원래 가로 세로 비율을 유지하기 위하면 운이 없을 것이다. 인터페이스 빌더를 사용해서는 다음과 같은 효과를 얻을 수 없다.

이미지 가로 세로 비율

불행하게도 현재 인터페이스 빌더는 뷰의 가로 세로 비율을 유지하는 제약조건을 만드는 방법을 제공하지 않는다. 이것을 하기 위해서는 프로그래밍 방식으로 제약조건을 만들고 설정해야 한다. iOS 6 튜토리얼의 “중급 오토 레이아웃” 에서 방법을 배울 것이다.

팁: Simulated Metrics아래의 Orientation 설정을 변경해서 UI를 가로 방향으로 보이게 미리보기를 할 수 있는 것을 보았다. 인터페이스 빌더에서 직접 뷰의 크기 변경 동작을 테스트 할 수 있다.

메인 뷰를 선택한다. Simulated Mertrics 아래에서 Size를 Freeform으로 설정한다. 원하는 어떤 모양으로 nib의 크기를 변경할 수 있게 만든다. 오토 레이아웃은 바로 레이아웃을 계산하여 보여준다.

Freeform

그러나 이것은 주의해라. 때때로 인터페이스 빌더는 크기를 변경할 때 새로운 제약조건을 추가한다. 여기에서는 오른쪽-아래에 Horizontal Space를 추가했다. nib의 경계가 벗어났을 때 기존의 제약조건을 삭제 할 수 있다.


반응형
Posted by 컴스터
,