AutoLayout을 코드로 구현하기
AutoLayout을 코드로 작성하는 방법을 공부하며 Apple 공식 문서를 번역하고 정리해봤습니다.
Programmatically Creating Constraints
제약 조건을 코드로 작성하기 위한 방법은 총 3가지가 존재한다: layout anchor
을 사용하거나, NSLayoutContraint
클래스를 사용하거나, Visual Format Language
를 사용하거나.
코드로 제약조건을 주기 위해선 반드시
translatesAutoresizingMaskIntoConstraints
의 값을 false로 지정해주어야 한다.
LayoutAnchor
NSLayoutAnchor
를 사용하기 위해서는 제약을 주고싶은 아이템의 anchor
프로퍼티에 접근해야 한다. 예를들어, view controller의 top과 bottom layout guides는 topAnchor
, bottomAnchor
과 heightAnchor
프로퍼티가 있다. 반면에 View는 edge
, center
, size
, baseline anchor
을 노출시킨다.
Layout Anchor
는 가독성이 좋고 컴팩트한 형식으로 제약조건을 생성한다. 아래 코드처럼 각기 다른 제약조건들을 생성하는 다양한 메소드들이 존재한다.
// Get the superview's layout
let margins = view.layoutMarginsGuide
// Pin the leading edge of myView to the margin's leading edge
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
// Pin the trailing edge of myView to the margin's trailing edge
myView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
// Give myView a 1:2 aspect ratio
myView.heightAnchor.constraint(equalTo: myView.widthAnchor, multiplier: 2.0).isActive = true
Layout Anchor
는 제약조건들을 생성하는 다양한 메소드들을 가지고, 각각의 메소드들은 결과에 영향을 주는 방정식의 요소들만 parameter로 전달된다.
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
위 코드와 방정식을 대응하면 다음과 같다:
Equation | Symbol |
Item 1 | myView |
Attribute 1 | leadingAnchor |
Relationship | constraintEqualToAnchor |
Multiplier | None (defaults to 1.0) |
Item 2 | margins |
Attribute 2 | leadingAnchor |
Constant | None (defaults to 0.0) |
또한, Layout Anchor
는 추가적인 type safety를 제공하는데 이로인해 잘못된 제약조건이 우연히 발생하는 것을 예방한다. 예를들어 horizontalAnchor
는 오직 horizontal과 대응되며, multiplier
는 size 제약조건을 위해서만 제공된다.
NSLayoutConstraint Class
제약조건을 생성하기 위해서 NSLayoutConstraint
클래스의 constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
메소드를 사용할 수 있다. 이 메소드는 제약 방정식을 명시적으로 코드로 변환해준다. 각 파라미터는 방정식과 대응된다. Layout Anchor
와는 다르게, 결과에 영항을 주지 않는 파라미터에도 값을 명시해야 한다. 따라서 코드의 가독성이 떨어진다는 특징이 있다. 또 특정 제약조건의 중요한 특성들을 강조하지 않기 때문에 중요한 디테일을 놓칠 가능성이 있다. 따라서 Layout Anchor API를 사용하는 것을 추천한다.
NSLayoutConstraint(item: myView,
attribute: .leading,
relatedBy: .equal,
toItem: view,
attribute: .leadingMargin,
multiplier: 1.0,
constant: 0.0).isActive = true
NSLayoutConstraint(item: myView,
attribute: .trailing,
relatedBy: .equal,
toItem: view,
attribute: .trailingMargin,
multiplier: 1.0,
constant: 0.0).isActive = true
NSLayoutConstraint(item: myView,
attribute: .height,
relatedBy: .equal,
toItem: myView,
attribute:.width,
multiplier: 2.0,
constant:0.0).isActive = true
Visual Format Language
이 방법은 문자열과 ASCII-art 을 사용하여 제약조건을 정의하는 방법이다. 제약조건의 표현을 시각적인 묘사로 표현한다. 다음은 Visual Format Language
의 장점과 단점이다:
-
AutoLayout은 Visual Format Language를 이용하여 제약조건들을 콘솔에 출력한다. 이러한 이유로 디버깅 메시지가 제약조건을 생성하기 위한 코드와 매우 유사하다.
-
Visual Format Language는 여러 제약조건들을 한번에 생성하고 매우 컴팩트한 표현을 사용한다.
-
Visual Format Language는 오직 유요한 제약조건들만 생성할 수 있게 한다.
-
이 표현 방법은 완전성보다 좋은 시각화에 초점을 두기 때문에 Visual Format Language가 표현할 수 없는 제약조건들이 존재한다.
-
컴파일러는 문자의 유효성을 검사하지 않기 때문에 오류는 런타임 테스팅에서만 발견된다.
Visual Format Language을 이용한 제약조건 생성 코드.
이 코드는 leading과 trailing 제약을 생성한다. Visual format language는 default sapcing을 사용하면 슈퍼뷰의 margin에 0-point 제약을 생성하기 때문에 앞서 보았던 예시들과 동일한 제약조건을 생성한다.
let views = ["myView" : myView]
let formatString = "|-[myView]-|"
let constraints = NSLayoutConstraint.constraints(withVisualFormat: formatString,
options: .alignAllTop,
metrics: nil,
views: views)
NSLayoutConstraint.activate(constraints)
위 코드에서는 가로 세로 비율 제약조건을 생성할 수 없는데, Visual format language는 슈퍼 뷰를 제외한 뷰가 두개 이상 존재해야 수직, 수평 제약조건을 줄 수 있다. 이러한 이유로 .alignAllTop
은 layout에 영항을 주지 않는다.
Visual Format Language를 사용해서 제약조건을 생성하려면:
-
view들로 이루어진 dictionary를 생성한다. 이 dictionary는 반드시
[String:view object]
형태로 구성되어야 한다. key값은 view에 대한 format string을 식별한다. -
(Optional) metrics dictionary를 생성한다. 이 dictionary는 반드시
[String: NSNumber]
형태로 구성되어야 한다. key는 format string의 constant 값을 대표한다. -
item의 단일 행과 열을 배치함으로써 format string을 생성한다.
-
NSLayoutContraint
클래스의constraintsWithVisualFormat:options:metrics:views:
메소드를 호출한다.이 메소드는 모든 제약조건을 담고있는 배열을 반환한다. -
NSLayoutContraint
클래스의activateConstraint:
메소드를 호출해서 모든 제약조건은 활성화 한다.