본문 바로가기
프로그래밍 놀이터/iOS

[iOS Study] 오토 레이아웃 : 프로그래밍으로 제약조건 만들기

by 돼지왕 왕돼지 2016. 3. 4.
반응형

 [iOS Study] 오토 레이아웃 : 프로그래밍으로 제약조건 만들기


출처 : 아론 힐리가스의 iOS 프로그래밍


+ (id)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c, + (NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views, 1000, ==, addConstracts, addConstracts:, addConstraints, Auto Layout, autoresizing mask, cgfloat, common ancester, constraintsWithVisualFormat:options:metrics:views:, Container, content compression resistance priority, content hugging priority, contraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:coinstant:, h:, ID, intrinsic content size, IOS, ios study, ios tutorial, iOS 프로그래밍, loadView, nib, No, NSAutoresizingMaskLayoutConstraint, NSDictionary, NSLayoutAttribute, NSLayoutAttributeBaseline, NSLayoutAttributeBottom, NSLayoutAttributeCenterX, NSLayoutAttributeCenterY, NSLayoutAttributeHeight, NSLayoutAttributeLeading, NSLayoutAttributeLeft, NSLayoutAttributeRight, NSLayoutAttributeTop, NSLayoutAttributeTriling, NSLayoutAttributeWidth, NSLayoutConstraint, NSLayoutRelation, translateAutoresizingMaskIntoConstraints, translated constraints, translatesAutoresizingMaskIntoConstraints, unable to simultaneously satisfy constraints, VfL, view1.attribute relation( ex) = ) view2.attribute * multiplier + constant, viewDidLoad, Visual Format Language, XIB, [iOS Study] 오토 레이아웃 : 프로그래밍으로 제약조건 만들기, _, 고유 콘텐츠 크기, 고정 크기 제약 조건, 공통 조상, 기본 변환 옵션, 문자열, 변환 제약조건, 불총족한 제야곶건, 뷰, 뷰 추가, 비율 제약 조건, 비주얼 포맷 언어, 상위뷰, 생략, 수직 제약 조건, 수평 제약 조건, 아론 힐리가스, 애플, 오토 레이아웃, 오토리사이징 마스크, 제약 조건, 제약조건, 제한, 컨테이너, 콘텐츠 축소 방지 우선순위, 콘텐츠 크기 유지 우선순위, 파이프 문자, 표시할 내용, 표준 간격, 프로그래밍, 현제 뷰


-

애플은 가능한 한 XIB 파일에서 뷰를 만들고 제한하는 것을 추천한다.

하지만 코드로 뷰를 만들면 프로그래밍으로 제약 조건을 적용해야 한다.



-

프로그래밍으로 뷰를 만들기 위해서는 loadView 메소드를 재정의한다.

NIB 파일을 로드하여 만들어진 뷰 계층구조에 뷰를 추가하고 제한하려면 viewDidLoad 메소드를 재정의한다.



-

변환 제약조건(translated constraints)에 관한 코드는 예전 시스템 인터페이스인 오토리사이징 마스크(autoresizing mask)와 함께 동작해야만 한다.

iOS 앱은 오토 레이아웃이 도입되기 전에 여러 크기의 화면에 따라 뷰를 조절하기 위해 오토리사이징 마스크를 사용했었다.


모든 뷰는 오토리사이징 마스크를 가진다.

기본적으로 iOS 는 오토리사이징 마스크와 일치하는 제약조건을 만들어 뷰에 추가한다.

이들 변환 제약조건은 레이아웃에서 명시적으로 지정한 제약조건과 때때로 충돌하여 불총족한 제약조건 문제를 일으킨다.

이 때는 translatesAutoresizingMaskIntoConstraints 프로퍼티를 NO 로 설정하여 기본 변환 옵션을 끄는 동작을 한다.



-

애플은 프로그래밍으로 제약조건을 만들 때 비주얼 포맷 언어(Visual Format Language, VFL) 이라는 특수 문법을 사용할 것을 추천한다.

이것으로 뷰를 제한할 것이다.

하지만 VFL 로 제약조건을 기술할 수 없는 경우가 있다.

이러한 경우에는 다른 방식을 사용해야 한다.



-

비주얼 포맷 언어는 보통 문자열로 제약조건을 기술하는 방법이다.

하나의 비주얼 포맷 문자열에 여러 제약조건을 기술할 수 있다.

하지만 단일 비주얼 포맷 문자열은 수직과 수평 제약조건 모두를 기술할 수는 없다.



-

다음은 비주얼 포맷 언어로 이미지뷰에 수평 공간 제약조건을 기술하는 방법이다.


@“H:|-0-[imageView]-0-|"


여기서 H: 는 수평 공간에 대한 제약조건임을 명시한다.

대괄호 안에는 뷰를 지정한다.

파이프 문자(|) 는 뷰의 컨테이너를 나타낸다.

따라서 이 이미지뷰는 좌측과 우측은 자신의 컨테이너에서 0포인트 떨어지게 된다.


뷰와 컨테이너(또는 다른 뷰) 사이의 포인트가 0이면 대시(-)와 0은 생략할 수 있다.


@“H:|[imageView]|"



-

수직 제약조건에 대한 문자열은 다음과 같다.


@“V:[dateLable]-8-[imageView]-8-[toolbar]"


대시로만 그 간격을 표시하면 표준 간격인 8포인트로 설정한다.

즉 다음 VFL 는 위의 VFL 과 같다.


@“V:[dateLabel]-[imageView]-[toolbar]"



-

고정 크기 제약조건의 문법은 간단히 뷰의 비주얼 포맷에서 괄호 안의 등호 연산자와 값을 추가하는 것이다.


@“V:[someView(==50)]"


이 뷰의 높이는 50포인트로 제한된다.



-

제약조건은 NSLayoutConstraint 클래스의 인스턴스이다.

제약조건을 프로그래밍으로 만들려면 명시적으로 하나 이상의 NSLayoutConstraint 인스턴스를 만들고 적당한 뷰 객체에 그 인스턴스를 추가해야 한다.

제약조건의 생성과 추가는 XIB 를 사용할 때는 한 단계이지만, 코드에서는 두 단계로 나뉜다.



-

NSLayoutConstraint 메소드들을 사용하여 비주얼 포맷 문자열로부터 제약조건을 만든다.


+ (NSArray *)constraintsWithVisualFormat:(NSString *)format

     options:(NSLayoutFormatOptions)opts

     metrics:(NSDictionary *)metrics

     views:(NSDictionary *)views


비주얼 포맷 문자열은 보통 여러 제약조건들을 만들기 때문에 이 메소드는 NSLayoutConstraint 객체들의 배열을 반환한다.


첫 번째 인자는 비주얼 포맷 문자열.

이후 두 인자는 우선 무시할 수 있지만 네 번째 인자는 필수 인자이다.


네번째 인자는 비주얼 포맷 문자열에서 이름과 뷰 계층구조상의 뷰 객체를 매핑한 NSDictionary 이다.

뷰를 가리키는 변수명으로 뷰 객체들을 참조한다.

변수명에 명시적으로 뷰를 연결시켜주어야 한다.



-

보통 변수명을 키로 사용하지만 뷰를 지정하기 위해 어떤 키든 사용할 수 있다.

한 가지 예외는 포맷 문자열에서 참조하는 뷰의 상위뷰(컨테이너)의 이름으로 예약된 | 문자이다.



-

제약조건은 UIView 메소드를 사용하여 명시적으로 추가하지 않는 한 레이아웃에 아무런 영향을 못 미친다.


- (void)addConstracts:(NSArray *)constraints







-

어느 뷰가 addConstracts: 메시지를 받아야 할까?

대개 뷰들의 가장 가까운 공통의 조상이 제약조건에 영향을 받는다.

어느 뷰에 제약조건을 추가할지 결정하기 위해 다음 규칙들을 따를 수 있다.


* 제약조건이 상위뷰가 같은 두 뷰에 영향을 준다면 그 제약조건은 그 뷰들의 상위뷰에 추가돼야 한다.

* 제약조건이 단 하나의 뷰에 영향을 준다면, 그 제약조건은 영향을 받는 그 뷰에 추가되어야 한다.

* 제약조건이 상위뷰는 다르지만 뷰 계층에서 좀 더 위의 공통 조상이 있는 두 뷰에 영향을 준다면, 첫 번째 공통 조상이 제약조건을 가진다.

* 제약조건이 뷰와 그 뷰의 상위뷰에 영향을 준다면 이 제약조건은 그 상위뷰에 추가될 것이다.



-

고유 콘텐츠 크기(intrinsic content size) 는 뷰가 표시할 내용을 기반으로 뷰의 크기가 어느 정도될 지에 대한 정보다.

예를 들어 라벨의 고유 콘텐츠 크기는 표시할 텍스트 크기에 기반을 둔다.

이미지뷰의 고유 콘텐츠 크기는 직접 선택한 이미지 크기이다.



-

오토 레이아웃은 각 뷰에 고유 콘텐츠 크기 제약조건을 만드는 것으로 이 정보를 고려한다.

이 제약조건은 다른 제약조건과 달리 두 개의 우선순위를 갖는다.

콘텐츠 크기 유지 우선순위(content hugging priority) 와 콘텐츠 축소 방지(content compression resistance priority) 우선순위이다.



-

Content hugging priority 는 오토 레이아웃에 뷰의 크기를 고유 콘텐츠와 비슷하게 유지할지에 관해 알려준다.

이 값이 1000이면 뷰가 고유 콘텐츠 크기보다 더 커지는 것을 절대 허용하지 않음을 의미한다.

값이 100보다 작다면 오토 레이아웃은 필요한 경우 뷰의 크기를 늘릴 수 있다.



-

Content compression resistance priority 는 오토 레이아웃에 뷰가 고유 콘텐츠보다 줄어들지에 관해 알려준다.

이 값이 1000이면 뷰가 고유 콘텐츠 크기보다 작아지는 것을 절대 허용하지 않음을 의미한다.

값이 1000보다 작다면 오토 레이아웃은 필요한 경우 뷰를 줄일 수 있다.



-

두 우선순위는 뷰의 높이와 너비에 각각 다른 우선순위를 설정할 수 있도록 수평값과 수직값을 구분한다.

이는 곧 뷰마다 총 네 개의 고유 콘텐츠 크기값을 만든다.



-

인터페이스 빌더에서 이 값들을 확인하고 편집할 수 있다.

인스펙터에서 Content Hugging Priority 와 Content Compression Resistance Priority 를 확인할 수 있다.



-

제약조건을 비주얼 포맷 문자열로 만들 수 없는 경우가 있다.

비율 기반의 제약 조건을 만들 때는 비주얼 포맷 언어를 사용할 수 없다.

뷰의 너비를 항상 그 높이의 1.5배가 되도록 하는 경우처럼 말이다.


다음 메소드를 사용하여 NSLayoutConstraint 인스턴스를 만들 수 있다.


+ (id)constraintWithItem:(id)view1

     attribute:(NSLayoutAttribute)attr1

     relatedBy:(NSLayoutRelation)relation

     toItem:(id)view2

     attribute:(NSLayoutAttribute)attr2

     multiplier:(CGFloat)multiplier

     constant:(CGFloat)c


이 메소드는 두 뷰 객체의 두 레이아웃 속성을 이용해 하나의 제약조건을 만든다.

multiplier 인자가 비율 기반의 제약조건을 만드는 핵심이다.

constant 는 간격 제약조건에서 사용했던 것처럼 고정된 포인트 숫자이다.



-

레이아웃 속성은 NSLayoutConstraint 클래스에 다음과 같은 상수로 정의된다.


NSLayoutAttributeLeft

NSLayoutAttributeRight

NSLayoutAttributeTop

NSLayoutAttributeBottom

NSLayoutAttributeWidth

NSLayoutAttributeHeight

NSLayoutAttributeBaseline

NSLayoutAttributeCenterX

NSLayoutAttributeCenterY

NSLayoutAttributeLeading

NSLayoutAttributeTriling



-

NSLayoutConstraint 는 아래와 같은 공식으로 해석 가능하다.


view1.attribute relation( ex) = ) view2.attribute * multiplier + constant



-

뷰에 단일 제약조건을 추가하기 위해서는 다음 메소드를 사용한다


- (void)addConstraint:(NSLayoutConstraint *)constraint


이 메시지를 받아야 할 뷰는 addConstraints:와 같은 논리를 적용하여 결정한다.



-

오토 레이아웃 이전에 iOS 앱들은 오토리사이징 마스크라는 레이아웃 관리 시스템을 사용했다.

뷰 각각은 뷰와 그 상위뷰 간의 관계를 제한하는 오토리사이징 마스크를 갖는다.

하지만 이 마스크는 형제 뷰들 간의 관계에는 영향을 미치지 않는다.


기본적으로 뷰는 오토리사이징 마스크를 기반으로 제약조건을 만들고 추가한다.

하지만 이러한 변환 제약조건들은 레이아웃에 직접 추가된 제약조건과 종종 충돌한다.

그 결과 불충족한 제약조건 문제가 발생한다.


이 문제를 막기 위해 오토리사이징 마스크의 변환 옵션을 끄는 것이 좋다.

view.translatesAutoresizingMaskIntoConstraints = NO;



-

오토 레이아웃과 오토리사이징이 충돌하면 “Unable to simultaneously satisfy constraints” 메시지가 보인다.



-

NSAutoresizingMaskLayoutConstraint 의 인스턴스는 오토리사이징 마스크의 변환 결과물이다.

오토 레이아웃 시스템은 NSAutoresizingMaskLayoutConstraint 대신에 우리가 직접 추가한 NSLayoutConstraint 인스턴스 중 하나를 무시하는 잘못된 선택을 할 수 있다.

이 경우 NSAutoresizingMaskLayoutConstraint 를 제거해주어야 한다.


하지만 명시적으로 변환 옵션을 비활성하여 이 제약조건이 추가되는 것을 막는 편이 좋다.

view.translateAutoresizingMaskIntoConstraints = NO;








반응형

댓글