🧑‍💻/Swift

[3rd] SkeletonUI 사용법

유리맥 2024. 1. 29. 22:47
반응형

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 이상 버전을 사용해 주세요!

이렇게 별 일 아닌 것 처럼 fixed를 써 놓다니 ㅠㅠ

SkeletonUI를 넣었더니 3개의 Package Dependency가 추가되었습니다.

 

2. 사용 예시 적용해 보기

SkeletonUI README 가이드에 나온 사용 예시를 실행해 보았습니다.

예시 1

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"]
                }
        }
    }
}

 

예시 2

이번엔 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로 한 번 감싼 형태입니다.

반응형