[Library] AutoLayout을 짧은 코드로 구현하려면, SnapKit
2021. 11. 16. 13:23ㆍFramework, Library
💡 SnapKit이란?
iOS 개발에서 UI를 코드로 구현할 때 (AutoLayout을 잡을 때) 편리하게 도와주는 라이브러리이다.
스토리보드나 SwiftUI를 이미 경험해본 사람에게 있어, 코드 베이스로만 UI를 구성한다는 것은 매우 겁나는 일이다.
나도 처음에는 편한 스토리보드, xib 등이 있는데, 굳이 코드로 UI를 짜야하는 이유가 뭐가 있을까? 했는데,
개발을 계속 공부하다 보니 어쩔 수 없이 코드 베이스로 개발을 해야 하는 상황이 생기게 되더라.
(협업, 유지 보수의 편리함, 속도 빠름, 파일 가벼움, conflict 발생 가능성이 적다 등의 장점 등등)
그렇다고 무작정 코드로 UI를 짜다보면, 라벨 하나를 넣더라도 코드의 길이가 엄청 길어지는 본인을 목격할 수 있었는데,
이 긴 코드를 조금이나마 줄이고자 등장한 것이 바로 오늘 배울 SnapKit 라이브러리다.
SnapKit 공식 문서를 참고하여 작성한 글이며,
이 글에서 다루지 못한 내용을 더 알아보고 싶다고 하면 아래 깃허브 링크에서 직접 확인해보길 바란다.
✍🏻 SnapKit 공식문서 살펴보기
💡iOS 레이아웃을 잡는 기초 방법, AutoLayout
Autolayout은 View 사이의 관계를 기반으로 레이아웃을 정의하는 방식이다.
제약 조건 ("위와 간격을 이렇게 줘!", "왼쪽과 간격을 이만큼 줘!"와 같은..)을 바탕으로 각 뷰의 크기를 결정하게 되기 때문에,
반대로 충분하지 않은 제약조건으로 레이아웃을 잡게 된다면, 해당 앱은 뷰의 크기를 결정할 수 없게 될 것이다.
그냥 View의 Width는 얼마이고 Height는 얼마이고 "화면 어느 부분에 띄워!" 같이 명시적으로 레이아웃을 잡는 것이 아니라,
굳이 다른 뷰와의 관계를 바탕으로 레이아웃을 잡는 Autolayout을 사용하는 가장 큰 이유는 외부와 내부 변경에 레이아웃을 동적으로 대응시키기 위해서이다.
우리는 하나의 레이아웃을 가지고
아이폰 mini 시리즈부터 아이폰 pro max 시리즈까지 다양한 크기의 화면에 대응할 수 있는 앱을 만들고 싶은데,
만약 화면 자체로 위치를 결정한다면, 아이폰 기기별로 화면 크기가 달라지기 때문에 일관적이지 않은 레이아웃이 잡히게 될 것이다.
그래서 화면 내에 있는 뷰와 뷰 사이의 관계를 이용해 레이아웃을 정의해 화면 크기가 달라지더라도 자연스럽게 대응할 수 있도록 설정해주는 것이다.
💡 NSLayoutConstraint로 잡은 레이아웃과 SnapKit으로 잡은 레이아웃 비교해보기
일단 바로 코드부터 비교해보자.
1번은 일반 코드로 AutoLayout을 잡을 때의 코드,
2번과 3번은 SnapKit을 사용해서 AutoLayout을 잡은 코드이다.
3번을 코드를 보면, 처음 레이아웃을 잡던 코드에 비해 코드의 길이가 극단적으로 짧아진 것을 확인할 수 있다.
추가로, 원래 레이아웃을 잡을 때 항상 해줘야 했던 아래 녀석을 SnapKit에서는 따로 작성하지 않아도 된다.
exampleView.translatesAutoresizingMaskIntoConstraints = false
*AutoresizingMask는 상위 뷰(SuperView)의 크기 변화에 따라 하위 뷰(SubView)들의 제약조건을 유연하게 재설정하는 개념
**뷰와 뷰 사이의 관계를 기반으로 레이아웃을 잡는 AutoLayout의 개념과 충돌되는 내용이므로 false를 시켜주는 것.
#1. 일반 NSLayout 속성을 사용해서 잡은 AutoLayout
exampleView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
exampleView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
exampleView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
exampleView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20),
exampleView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
])
#2. SnapKit을 사용해서 잡은 AutoLayout
exampleView.snp.makeConstraints { make in
make.top.equalTo(view).offset(20)
make.left.equalTo(view).offset(20)
make.bottom.equalTo(view).offset(-20)
make.right.equalTo(view).offset(-20)
}
#3. SnapKit을 사용해서 잡은 AutoLayout (코드를 더 짧게 줄인 버전)
exampleView.snp.makeConstraints { make in
make.edges.equalTo(view).inset(UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20))
}
💡 SnapKit 읽는법
SnapKit은 코드가 직관적이다. 그냥 순서대로 해석하면 된다.
make.top.equalTo(view).offset(20)
예를 들어, 위 코드 같은 경우에는 "만들어 (SnapKit에서 클로저 매개변수 이름으로 흔히 쓰인다. 매개변수 이름을 생략하고 클로저 단축인자 이름 $0을 붙여도 된다.) -> 상단을 -> 뷰랑 같게 -> 대신, offset은 20을 줘!"라고 해석을 하면 된다.
💡 SnapKit에서 사용하는 AutoLayout 용어(위치)를 익혀보자
NSLayout에서 | SnapKit에서 | 설명 |
NSLayoutConstaint.Attribute.left | view.snp.left | 좌측 (만국공통) |
NSLayoutConstaint.Attribute.right | view.snp.right | 우측 (만국공통) |
NSLayoutConstaint.Attribute.top | view.snp.top | 정렬 사각형의 상단 |
NSLayoutConstaint.Attribute.bottom | view.snp.bottom | 정렬 사각형의 하단 |
NSLayoutConstaint.Attribute.leading | view.snp.leading | 리딩, 좌측 (읽는 방향에 따라 달라질 수 있음, flip 고려) |
NSLayoutConstaint.Attribute.trailing | view.snp.trailing | 트레일링, 우측 (읽는 방향에 따라 달라질 수 있음, flip 고려) |
NSLayoutConstaint.Attribute.width | view.snp.width | 정렬 사각형의 너비 |
NSLayoutConstaint.Attribute.height | view.snp.height | 정렬 사각형의 높이 |
NSLayoutConstaint.Attribute.centerX | view.snp.centerX | 수평 중심 |
NSLayoutConstaint.Attribute.centerY | view.snp.centerY | 수직 중심 |
NSLayoutConstaint.Attribute.lastBaseline | view.snp.lastBaseline | 텍스트의 하단 (텍스트가 들어가는 뷰) |
💡간격을 잡기 위한 두 가지 방법, offset과 inset
offset은 element와의 간격에 사용하고, inset은 superview와의 간격에 사용하는 방법이다.
이게 무슨 말이냐면, offset은 겉을 기준으로, inset은 안을 기준으로 간격을 잡는다는 뜻이다.
아직, 무슨 말인지 잘 모르겠다면, 아래 그림으로 비교해보자.
우리가 주목해야 할 부분은 우측(right)과 하단(bottom) 부분이다.
첫 번째 사진은 offset(-100)을 사용했다.
offset은 겉 레이아웃을 기준으로 판단하기 때문에, Subview가 겉보다 안쪽에 있을 경우에 음수 값을 넣어줘야 한다.
make.right.equalTo(view).offset(-100)
make.bottom.equalTo(view).offset(-100)
두 번째 사진은 inset(100)을 사용했다.
inset을 기준으로 잡을 때는 안쪽으로 향하는 모든 방향이 양수의 값을 갖기 때문에 모든 간격을 양수로 잡아줘야 올바르게 잡힌다.
make.right.equalTo(view).inset(100)
make.bottom.equalTo(view).inset(100)
그래서 만약 right, bottom에 대해서 inset을 음수로 잡거나, outset을 양수로 잡게 되면 마지막 사진처럼 레이아웃 바깥쪽으로 넘어가 간격이 잡히게 되는 모습을 확인할 수 있겠다.
offset | inset |
element와의 간격에 사용 | superview와의 간격에 사용 |
"겉"을 기준으로 간격을 잡음 | "안"을 기준으로 간격을 잡음 |
좌측 상단(0, 0)을 기준으로 확장되는 "겉"을 의미 -> 오른쪽에서 왼쪽, 아래에서 위 방향은 음수값(-)을 가짐 |
안쪽 방향에 대해서 모두 양수값을 가짐 -> 오른쪽에서 왼쪽, 아래에서 위 방향도 양수값(+)을 가짐 |
💡크거나 같게, 작거나 같게, 같게
*위치에 해당하는 부분에는 위에서 설명한 AutoLayout 위치(용어)가 들어가게 됩니다.
- make.위치.greaterThanOrEqualTo(대상 or 상수값): 대상 or 상수값보다 크거나 같은 레이아웃 조건을 설정
- make.위치.lessThanOrEqualTo(대상 or 상수값): 대상 or 상수값보다 작거나 같은 레이아웃 조건을 설정
- make.위치.equalTo(대상 or 상수값): 대상 or 상수값과 같은 레이아웃 조건을 설정
💡우선순위 설정
.priority를 사용해서 우선순위를 지정할 수 있다.
make.top.equalTo(view).priority(600)처럼 제약 조건 뒤에 붙이는 방식으로 사용하며,
.low .medium .high .required 같은 키워드를 사용해서 지정할 수도 있다.
💡edges, size, center를 사용해서 일괄적으로 코드 길이 줄이기
가장 위에서 살짝 언급한 대로, SnapKit을 사용해서 코드의 길이를 극단적으로 줄일 수도 있다.
그게 바로 edges, size, center라는 키워드를 사용하는 방법이다.
edges: 가장자리를 일괄적으로 제약 조건 생성 (top, left, right, bottom)
size: 크기를 일괄적으로 제약 조건 생성 (width, height)
center: 중심점을 일괄적으로 제약 조건 생성 (centerX, centerY)
// 예시) edges를 사용해서 가장자리 제약조건을 일괄적으로 설정한 경우
exampleView.snp.makeConstraints { (make in
make.top.equalTo(view).inset(100)
make.left.equalTo(view).inset(100)
make.right.equalTo(view).inset(100)
make.bottom.equalTo(view).inset(100)
}
exampleView.snp.makeConstraints { make in
make.edges.equalTo(view).inset(UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20))
}
💡레이아웃의 업데이트
UpdateConstraints: 상수값만 업데이트하고 싶을 때
RemakeConstraints: 상수값과 기준이 되었던 view도 업데이트 하고 싶을 때, 이때 기존 상수값은 없어진다.
모두 위에서 쓴 것처럼
대상 뷰.snp.업데이트 { 클로저 } 형태로 사용하면 된다 ^__^
💡Reference
'Framework, Library' 카테고리의 다른 글
[Social Login] Access Token과 Refresh Token, 그리고 Auto Login까지 (6) | 2022.01.05 |
---|---|
[Social Login] 소셜 로그인을 구현해보자! 2탄 - Apple 로그인 (0) | 2021.12.27 |
[Library] Alamofire의 단점을 보완한 네트워킹 라이브러리, Moya (0) | 2021.12.17 |
[Library] 코딩 컨벤션을 자동으로 도와주는, SwiftLint 적용방법 (0) | 2021.10.02 |
[Social Login] 소셜 로그인을 구현해보자! 1탄 - 카카오톡 로그인 (0) | 2021.09.22 |