[3rd] SkeletonUI 사용법
Framework를 만드는 일은 해봤어도 3rd Party Framework 사용은 많이 안해봐서
새 프로젝트에 들어갈 때 공부해야할 내용이 많더라구요.
하나씩 파헤쳐보려 합니다. 러닝 커브가 낮은 Framework 부터...
Skeleton 이란?
사용자에게 데이터 로딩중임을 알리고, 데이터 로딩 후에도 자연스럽게 화면이 전환되는 컴포넌트입니다.
UIKit을 사용한다면 https://github.com/Juanpe/SkeletonView 를 많이 사용하는 것 같더라구요.
저는 SwiftUI에서 사용할 수 있는 SkeletonUI를 살펴보도록 하겠습니다.
1. Swift Package Manager 추가
URL : https://github.com/CSolanaM/SkeletonUI
2.0.0 버전 사용 시 앱 메모리가 빠른 속도로 증가하는 이슈가 있어서 꼭 2.0.1 이상 버전을 사용해 주세요!
SkeletonUI를 넣었더니 3개의 Package Dependency가 추가되었습니다.
2. 사용 예시 적용해 보기
SkeletonUI README 가이드에 나온 사용 예시를 실행해 보았습니다.
skeleton에 true/false 값으로 상태를 체크하고, true이면 그라데이션 애니메이션을 적용해서 보여줍니다.
5초 뒤에 users에 값을 셋팅해서 skeleton을 종료합니다.
import SkeletonUI
import SwiftUI
struct UsersView: View {
@State var users = [String]()
var body: some View {
Text("Finished requesting \(users.count) users!")
.skeleton(with: users.isEmpty)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.users = ["John Doe", "Jane Doe", "James Doe", "Judy Doe"]
}
}
}
}
이번엔 SkeletonList를 사용해서 TableView 형태를 Skeleton으로 표시해 봅니다.
List 목록에 표시할 양 (quantity) 이나 shape, 라인 수 등 지정할 수 있네요.
import SkeletonUI
import SwiftUI
struct User: Identifiable {
let id = UUID()
let name: String
}
struct UsersView: View {
@State var users = [User]()
var body: some View {
SkeletonList(with: users, quantity: 6) { loading, user in
Text(user?.name)
.skeleton(with: loading,
animation: .pulse(),
appearance: .solid(color: .red, background: .blue),
shape: .rectangle,
lines: 3,
scales: [1: 0.5])
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.users = [User(name: "John Doe"),
User(name: "Jane Doe"),
User(name: "James Doe"),
User(name: "Judy Doe")]
}
}
}
}
3. 활용하기
위 예시에서 값을 좀 바꿔보았습니다. 이게 더 원래 보던 스켈레톤의 모습 같네요.
- animation : pulse > linear
- appearance : solid > gradient
- shape : rectangle > capsule
- scales : [1: 0.5] > [0: 0.3, 1: 0.5, 2: 0.7]
scales는 [인덱스: 영역비율]라고 생각하시면 됩니다.
lines 값이 3이니 3줄이 표시될 텐데,
[0번째 라인은 0.3, 1번째 라인은 0.5, 2번째 라인은 0.7] 만큼 영역을 차지해서 보여줘라! 라는 뜻이죠.
(혹시나 하고 lines에 1를 넣어 봤는데 1줄만 표시되고 1번째, 2번째 인덱스 값은 무시되네요.)
struct UsersView: View {
@State var users = [User]()
var body: some View {
SkeletonList(with: users, quantity: 4) { loading, user in
Text(user?.name)
.skeleton(with: loading,
animation: .linear(),
appearance: .gradient(angle: .degrees(30)),
shape: .capsule,
lines: 3,
scales: [0: 0.3, 1: 0.5, 2: 0.7])
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.users = [User(name: "John Doe"),
User(name: "Jane Doe"),
User(name: "James Doe"),
User(name: "Judy Doe")]
}
}
}
}
나는 SkeletonList 안쓰고 싶고 원래 쓰던 List가 있다. 그 안에 커스텀해서 적용하고 싶다!
하신다면 다음과 같이 수정할 수 있겠습니다.
struct UsersView: View {
@State var users = [User]()
let quantity = 3
var body: some View {
List(0..<(users.isEmpty ? quantity : users.count), id: \.self) { index in
let username = users.count > index ? users[index].name : ""
Text(username)
.skeleton(with: users.isEmpty,
animation: .linear(),
appearance: .gradient(angle: .degrees(30)),
shape: .capsule,
lines: 1)
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.users = [User(name: "John Doe"),
User(name: "Jane Doe"),
User(name: "James Doe"),
User(name: "Judy Doe")]
}
}
}
}
SkeletonList 구현부를 보면 List로 한 번 감싼 형태입니다.