<4주차> 오토레이아웃을 코드로 작성하는 방법은 무엇인가? (3가지)
Closed this issue · 8 comments
1. NSLayoutConstraint 사용
NSLayoutConstraint.activate([NSLayoutConstraint(item: UIButton, attribute: .centerX, relatedBy: .equal, toItem: UIView, attribute: .centerX, multiplier: 1.0, constant: 0.0), ...])
2. Visual Format Language 사용
NSLayoutConstraint.activate([NSLayoutConstraint.constraints(withVisualFormat: "H:[button(200)]", options: .alignAllCenterX, metrics: nil, views: ["button": button]), ...)
3. Anchor 사용
NSLayoutConstraint.activate([button.centerXAnchor.constraint(equalTo: view.centerXAnchor), ...])
4. 이 외에 SnapKit과 같은 라이브러리를 사용할 수 있다.
NSLayoutConstraint
제약 기반 레이아웃 시스템에서 충족해야하는 두 사용자 인터페이스(UI) 개체 간의 관계입니다.
item1.attribute1 = multiplier × item2.attribute2 + constant
button2.leading = 1.0 × button1.trailing + 8.0
//다른 아이템과의 관계로서 layout 정의
Anchor
translatesAutoresizingMastIntoConstraints = false
//기존의 오토리사이징마스크와 충돌을 없애기 위해 설정
var constraintY: NSLayoutConstraint
constraintY = button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
Visual Format Language (처음 알게 됨)
기호 및 문자열로 레이아웃 관계 표시
Vertical Layout
V:[topField]-10-[bottomField]
Visual Format Guide
-
NSLayoutAnchor
사용
원래는NSLayoutConstraint
자체를 사용해서 오토레이아웃을 적용했다. 그러나 가독성도 좋지 않고 사용법이 조금 어려워서 새롭게 Apple에서 내준 방식이다. -
NSLayoutConstaint
사용
방법을 보면 복잡하다. 한 눈에 보기 편하지 않다!
convenience init(item view1: Any,
attribute attr1: NSLayoutConstraint.Attribute,
relatedBy relation: NSLayoutConstraint.Relation,
toItem view2: Any?,
attribute attr2: NSLayoutConstraint.Attribute,
multiplier: CGFloat,
constant c: CGFloat)
Visual Format Language
기본적으로 보기좋게 만들려고 쓰는 방법이다.
let views = ["redView": redView,
"blueView": blueView,
"greenView": greenView]
let format1 = "V:|-[redView]-8-[greenView]-|"
let format2 = "H:|-[redView]-8-[blueView(==redView)]-|"
let format3 = "H:|-[greenView]-|"
var constraints = NSConstraint.constraints(withVisualFormat: format1,
options: alignAllLeft,
matrics: nil,
views: views)
constraints += NSConstraint.constraints(withVisualFormat: format2,
options: alignAllTop,
matrics: nil,
views: views)
constraints += NSConstraint.constraints(withVisualFormat: format3,
options: []
matrics: nil,
views: views)
NSConstraint.activateConstraints(constraints)
출처 : 블로그
Anchor
사용하기
myView.translatesAutoresizingMastIntoConstraints = false
let margins = view.layoutMarginsGuide
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).active = true
myView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).active = true
myView.heightAnchor.constraint(equalTo: myView.widthAnchor, multiplier: 2.0)
LayoutConstraints
사용하기
가독성이 떨어지는 단점이 있습니다.
NSLayoutConstraint(item: myView, attribute: .leading, relatedBy: .Equal, toItem: view, attribute: .leadingMargin, multiplier: 1.0, constant: 0.0).isActive = true
- Visual Format 사용하기
설명하고자 하는 레이아웃의 시각적인 표현을 제공하는 방식입니다. 읽을 수 있도록 설계되어 있으며 뷰는 대괄호로 표시되고 뷰간의 연결은 하이픈(또는 뷰들을 떨어뜨리는 숫자에 의해 두개의 분리된 하이픈)을 사용합니다
let views = ["myView": myView]
let formatString = "|-[myView]-|"
let constraints = NSLayoutConstraint.constraintsWithVisualFormat(formatString,
options: .AlignAllTop,
metrics: nil,
views: views)
NSLayoutConstraint.activateConstraints(constraints)
Parameter | Description |
---|---|
Format | 제약 조건을 시각적 형식으로 나타낸 문자열 |
options | 객체들의 속성, 레이아웃 방향 설명 |
metrics | 상수 집합. 키는 문자열, 값은 NSNumber 객체 |
views | 뷰 집합. 키는 문자열, 값은 뷰 객체 |
내가 보려고 정리하는 오토 레이아웃 속성
제목 | 내용 |
---|---|
Width | 너비 |
Height | 높이 |
Top | 정렬 사각형의 상단 |
Bottom | 정렬 사각형의 하단 |
Baseline | 텍스트의 하단 |
Leading | 텍스트 시작 |
Trailing | 텍스트 끝 |
CenterX | 수평 중심 |
CenterY | 수평 하단 |
3가지가 존재하고
NSLayoutConstraint, Visual Format Language, NSLayoutAnchor를 사용한 레이아웃구현
NSLayoutConstraint
인터페이스 객체간에 레이아웃 관계를 나타냄
item1.attribute1 = multiplier × item2.attribute2 + constant
Visual Format Language
레이아웃의 시각적 표현
뷰는 대괄호 뷰간연결은 하이픈을 사용
NSLayoutConstraint를 이용해서 생성
NSLayoutAnchor
NSLayoutConstraint가 복잡하고 사용법이 어려워서 새로나온 클래스
NSLayoutConstraint 객체를 만들어내는 팩토리 클래스
간결하고 명확하게 사용가능
NSLayoutXAxisAnchor
- 수평 제약
NSLayoutYAxisAnchor
- 수직 제약
NSLayoutDimension
- 너비, 높이
aView.leadingAnchor.constraint(equalTo: leftLabel.leadingAnchor,constant: 10).isActive = true
Visual Format Language, NSLayoutAnchor는 결과적으로 NSLayoutConstraint를 사용
비교코드
func layout() {
view.addSubview(aView)
view.addSubview(bView)
aView.translatesAutoresizingMaskIntoConstraints = false
bView.translatesAutoresizingMaskIntoConstraints = false
aView.backgroundColor = .blue
bView.backgroundColor = .red
// Visual Format
let views: [String : Any] = ["a": aView,
"b": bView]
let format1 = "H:|-8-[a]-8-|"
let format2 = "H:|-30-[b]-30-|"
let format3 = "V:|-20-[a(100)]"
let format4 = "V:[a]-20-[b(100)]"
var constraint = NSLayoutConstraint.constraints(withVisualFormat: format1,
options: [],
metrics: nil,
views: views)
constraint += NSLayoutConstraint.constraints(withVisualFormat: format2,
options: [],
metrics: nil,
views: views)
constraint += NSLayoutConstraint.constraints(withVisualFormat: format3,
options: [],
metrics: nil,
views: views)
constraint += NSLayoutConstraint.constraints(withVisualFormat: format4,
options: [],
metrics: nil,
views: views)
view.addConstraints(constraint)
//NSLayoutConstraint
NSLayoutConstraint.init(item: aView,
attribute: .leading,
relatedBy: .equal,
toItem: view,
attribute: .leading,
multiplier: 1.0,
constant: 8).isActive = true
NSLayoutConstraint.init(item: aView,
attribute: .top,
relatedBy: .equal,
toItem: view,
attribute: .top,
multiplier: 1.0,
constant: 20).isActive = true
NSLayoutConstraint.init(item: aView,
attribute: .trailing,
relatedBy: .equal,
toItem: view,
attribute: .trailing,
multiplier: 1.0,
constant: -8).isActive = true
NSLayoutConstraint.init(item: aView,
attribute: .height,
relatedBy: .equal,
toItem: nil,
attribute: .height,
multiplier: 1.0,
constant: 100).isActive = true
NSLayoutConstraint.init(item: bView,
attribute: .leading,
relatedBy: .equal,
toItem: view,
attribute: .leading,
multiplier: 1.0,
constant: 30).isActive = true
NSLayoutConstraint.init(item: bView,
attribute: .top,
relatedBy: .equal,
toItem: aView,
attribute: .bottom,
multiplier: 1.0,
constant: 20).isActive = true
NSLayoutConstraint.init(item: bView,
attribute: .trailing,
relatedBy: .equal,
toItem: view,
attribute: .trailing,
multiplier: 1.0,
constant: -30).isActive = true
NSLayoutConstraint.init(item: bView,
attribute: .height,
relatedBy: .equal,
toItem: nil,
attribute: .height,
multiplier: 1.0,
constant: 100).isActive = true
//NSLayoutAnchor
aView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 8).isActive = true
aView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -8).isActive = true
aView.topAnchor.constraint(equalTo: view.topAnchor,constant: 20).isActive = true
aView.heightAnchor.constraint(equalToConstant: 100).isActive = true
bView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 30).isActive = true
bView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -30).isActive = true
bView.topAnchor.constraint(equalTo: aView.bottomAnchor,constant: 20).isActive = true
bView.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
세가지가 존재함니다
- Anchor 타입
- NSLayoutConstraint
- VFL
우선
스토리보드에 올라갈때는 translatesAutoresizingMaskIntoConstraints
속성이 자동으로 false로 반영됩니당
하지만 코드로 짤때는 손수 translatesAutoresizingMaskIntoConstraints = fase
를 해 주어야 해요
- Anchor 타입
NSLayoutConstraint.activate([
alertView.centerYAnchor.constraint(equalTo: window.centerYAnchor),
alertView.leadingAnchor.constraint(equalTo: window.leadingAnchor, constant: 20),
alertView.trailingAnchor.constraint(equalTo: window.trailingAnchor, constant: -20),
])
특정 Anchor를 지정해서 오토레이아웃을 잡는 방식인데, 배열 바깥에서 알 수 있듯 NSLayoutConstraint를 사용해서 active 시킵니당
특징
- 사용하기에 편하고 좋다
- 제일 많이 쓰는듯
단점
- 코드베이스 기반의 한계겠지만 가독성이 뛰어난 편은 절대 아님
- NSLayoutConstraint 타입
일단 코드를 보면
NSLayoutConstraint(item: alertView,
attribute: .leading,
relatedBy: .equal,
toItem: messageLabel,
attribute: .leading,
multiplier: 1.0,
constant: 8).isActive = true
이런식으로 item, toItem을 지정해서 짭니다. 특징 고고
특징
- 얘도 역시 짤때는 나쁘지 않습니다
- 가독성은 그래도 Anchor보다 나음 ( 슥 읽으면 됨 )
단점
- 조금 길고 multiplier 이거는 잘 안 쓰잖아요?
그래서 저는 이런 커스텀메소드를 만들어서 쓰는뎅 좋은듯해요
extension UIView {
// 1
func constraint(to view: UIView,
attribute: NSLayoutConstraint.Attribute,
secondAttribute: NSLayoutConstraint.Attribute,
inset: CGFloat = 0) {
self.translatesAutoresizingMaskIntoConstraints = false
let c = NSLayoutConstraint(item: self,
attribute: attribute,
relatedBy: .equal,
toItem: view,
attribute: secondAttribute,
multiplier: 1,
constant: inset)
c.isActive = true
}
// 2
func constraint(_ anchor: NSLayoutDimension, constant: CGFloat) {
self.translatesAutoresizingMaskIntoConstraints = false
anchor.constraint(equalToConstant: constant).isActive = true
}
이렇게 해놓고
// 1
dismissButton.constraint(to: alertView,
attribute: .trailing,
secondAttribute: .trailing,
inset: -12)
dismissButton.constraint(to: alertView,
attribute: .top,
secondAttribute: .top,
inset: 12)
// 2
dismissButton.constraint(widthAnchor, constant: 44)
dismissButton.constraint(heightAnchor, constant: 44)
이런식으로 씁니당. 나쁘지 않아요~~
- Visual Format Language
그.. VFL은 올해 초에 한참 많이 봤던 유튜바 Brian Voong씨가 자주 사용해서 제가 카톡방에 무러본적이 있는데
다양한 반응이 있었습니다
밑에분이 특이한거고 거의 안쓰시는듯 이름을 가려야되나 나중에 가리겟습니다
또한 오토레이아웃 잘못 잡으면 디버깅창에 뜨는 오토레이아웃 에러메시지가 VFL로 뜹니다!!! 그래서 배워놓으면 좋을듯 한데 저는 모름
이번 기회에 한번 써봤습니다~!
H:|-[find]-[findNext]-[findField(>=20)]-|
이런 코드를 뜯어볼게요
H: (Horizontal) // V는 vertical
| (pipe) // superview
- (dash) // 표준 스페이싱 (8 points)
[] (brackets) // 대상 객체 (UILabel, UIButton, uiview ~)
() (parentheses) // 객체의 사이즈 제약
특징
- 사용하시는분 말로는 이만큼 편한게 없다고 합니다
- 가독성도 꽤 높습니다
단점
- 너무나 많은 준비물 코드
2 .팀원중 한명만 사용할 줄 알면 못 씀
https://iosexample.com/ios-auto-layout-with-json-and-vfl/
JSONLayout이라는 라이브러리가 있는데 이게 되네...
2019년도 Let's Swift 강의
https://www.youtube.com/watch?v=EzjyuEf61Vo&list=PLAHa1zfLtLiOiVcIUwWZbOgN9x3cTjRqN&index=9
를 기반으로 작성합니당~
- NSLayoutConstraint
Layout을 code로 작성하기 위해서 나온 레이아웃 작성 방식
아래의 예시로 보면 알수있겠지만 코드 한줄 한줄 의미하는게 한눈에 보이고 좋지만
코드의 가독성이 좋지 않다.
NSLayoutConstraint.activate([
NSLayoutConstraint(item: button,
attribute: .centerX,
relatedBy: .equal,
toItem: view,
attribute: .centerX,
multiplier: 1.0,
constant: 0.0),
NSLayoutConstraint(item: button,
attribute: .width,
relatedBy: .equal,
toItem: nil,
attribute: .height,
multiplier: 1.0,
constant: 200.0),
NSLayoutConstraint(item: button,
attribute: .top,
relatedBy: .equal,
toItem: view,
attribute: .topMargin,
multiplier: 1.0,
constant: 100.0),
NSLayoutConstraint(item: button,
attribute: .height,
relatedBy: .equal,
toItem: view,
attribute: .height,
multiplier: 1.0,
constant: 80.0)
])
- Visual Format Language
아직 사용해본적은 없는 레이아웃 구조를 잡는 방식이지만, 익숙해지면... 간편할것만 같은 작성 방법...
문법에 대한 공부가 필요함... *** 제일 큰 단점
NSLayoutConstraint.activate([
NSLayoutConstraint.constraints(withVisualFormat: "H:[button(200)]",
options: .alignAllCenterX,
metrics: nil,
views: ["button": button]),
NSLayoutConstraint.constraints(withVisualFormat: "V:[sv]-(<=1.0)-[button(80)]",
options: .alignAllCenterX,
metrics: nil,
views: ["sv": view!, "button": button]),
NSLayoutConstraint.constraints(withVisualFormat: "V:|-(100)-[button(80)]",
options: .alignAllTop,
metrics: nil,
views: ["button": button])
].flatMap({ $0 }))
- Anchor
NSLayoutConstraint으로 작성한 코드 방식이 굉장히 가독성이 떨어지기 때문에 나온 레이아웃 코드 방식
하지만 이것 또한 코드가 매우매우 길어짐...
위 두개에 비해서는 가독성이 나쁘지 않는 듯!
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.widthAnchor.constraint(equalToConstant: 200.0),
button.topAnchor.constraint(equalTo: view.topAnchor, constant: 100.0),
button.heightAnchor.constraint(equalToConstant: 80.0)
])
- NSLayoutAnchor
Anchor라는 개념을 통해 NSLayoutConstraint를 생성시킬 수 있다.
Anchor와 관련된 프로퍼티
var constraints: [NSLayoutConstrint] // 뷰에 부여한 제약사항들을 담은 배열
var bottomAnchor: NSLayoutYAxisAnchor { get } // 뷰 프레임의 하단부 레이아웃 앵커
var centerXAnchor: NSLayoutXAxisAnchor { get } // 뷰 프레임의 수평 중심부 레이아웃 앵커
var centerYAnchor: NSLayoutYAxisAnchor { get } // 뷰 프레임의 수직 중심부 레이아웃 앵커
var heightAnchor: NSLayoutDimension { get }// 뷰 프레임의 높이를 가리키는 레이아웃 앵커
var leadingAnchor: NSLayoutXAxisAnchor { get } // 뷰 프레임의 리딩을 가리키는 레이아웃 앵커
var topAnchor: NSLayoutYAnchor { get } // 뷰 프레임의 상단부 레이아웃 앵커
var trailingAnchor: NSLayoytXAxisAnchor { get } // 뷰 프레임의 트레일링을 가리키는 레이아웃 앵커
var widthAnchor: NSLayoutDimension { get }// 뷰 프레임의 넓이를 가리키는 레이아웃 앵커
직접 사용한 예제
// Set Label’s width
var widthConstraint: NSLayoutConstraint
widthConstraint = label.widthAnchor.constraint(equalTo: Button.widthAnchor, multiplier: 2.0)
widthConstraint.isActive = true
다음을 통해 NSLayoutConstraint를 생성하여 isActive 해주거나
NSLayoutConstraint.activate([
label.widthAnchor.constraint(equalTo: Button.widthAnchor, multiplier: 2.0)
])
다음과 같이 배열을 통해 전체를 active 시켜주는 방법이 있다.
- NSLayoutConstraint
직접적으로 NSLayoutConstraint인스턴스를 생성하는 방법
centerY = NSLayoutConstraint(item: button,
attribute: .centerY,
relatedBy: .equal,
toItem: self.view,
attribute: .centerY,
multiplier: 0.8,
constant: 0)
다음과 같이 오토레이아웃 방정식의 각 속성들을 지정하여 생성할 수 있다. active해주는 방식은 anchor방식과 유사하다고 볼 수 있다.
- Visual Format Language
VFL에서 사용 가능한 기호 및 문자열의 의미
‘|’ : superView 입니다.
‘-‘ : 표준 간격입니다. 기본은 8포인트 입니다.
‘==‘ : 같은 너비 입니다.
-10- : 사이의 간격이 10포인트입니다.
‘<=50’ : 50보다 작거나 같습니다.
‘>=50’ : 50보다 크거나 같습니다.
‘@750 : 우선도를 지정할 수 있다.
‘H’ : 수평 방향 입니다.(가로)
‘V’ : 수직 방향 입니다.(세로)