はじめに
List
やLazyVGrid
などの画面初期化時にリスト数が決まっていないViewに対し、YoutubeやFacebookで使われているような、ロード中のスケルトンを表示する方法を書いてみました。
ちなみにスケルトンスクリーンとは、このようなやつです。
実装イメージは下記です。
結論
当たり前っちゃ当たり前なのですが、
リスト形式の場合、画面表示時には、表示数が0件なので、スケルトンを適用しても効果がありません。
なので、適当なDummyデータを作っておき、APIなどで本来表示すべき値が取得できたら、Dummyデータと置き換えて表示してあげればうまくいきます。
実装
結論で書いたことが、全てですので、簡単な実装だけご紹介します。
実際にAPIなどへリクエストすることを想定し、Combine
を利用していますが、ここではそれについては触れません。
Combine
については、以前この記事で書かせていただきましたのでもし良かったら、ご覧いただければと思います。
まず、ロード中にスケルトンを表示するためには、redacted(reason:)
が標準で用意されているので、それを使います。
struct UserInfo: Identifiable {
let id: Int
let name: String
let age: Int
let email: String
}
class ContentViewModel: ObservableObject {
var cancellables = Set<AnyCancellable>()
@Published var userInfoList = dummyData
@Published var isFirstLoading = true
let fetchUserSubject = PassthroughSubject<Void, Never>()
static var dummyData: [UserInfo] {
return (1...10).map {
UserInfo(id: $0, name: "DummyName", age: 99, email: "dummy@test.com")
}
}
init() {
fetchUserSubject
.flatMap({ _ -> AnyPublisher<[UserInfo], Never> in
let userInfoList = (1...3).map {
UserInfo(id: $0, name: "RealName", age: 10, email: "real@real.com")
}
return Just(userInfoList).eraseToAnyPublisher()
})
.delay(for: 2, scheduler: DispatchQueue.main)
.sink(receiveValue: { userInfoList in
self.userInfoList = userInfoList
self.isFirstLoading = false
})
.store(in: &cancellables)
}
}
static var dummyData: [UserInfo]
を定義して、@Published var userInfoList = dummyData
のようにして、初期化時はダミーデータが入るようにします。
ViewModelでは、let fetchUserSubject = PassthroughSubject<Void, Never>()
を購読していますので、View側から値がsend()
されたら、リアルデータをクラス変数にセットする処理が走ります。
本来は、APIによってデータを取得しますが、今回は、2秒後に固定値のリアルデータが帰ってくるように、処理を書いています。
struct ContentView: View {
@ObservedObject var viewModel = ContentViewModel()
var body: some View {
VStack {
List(viewModel.userInfoList) { user in
HStack {
Text(user.name)
Spacer()
Text("\(user.age)歳")
Spacer()
Text(user.email)
}
}
.redacted(reason: viewModel.isFirstLoading ? .placeholder : [])
}
.onAppear {
viewModel.fetchUserSubject.send(())
}
}
}
.redacted(reason: viewModel.isFirstLoading ? .placeholder : [])
をList
に対して適用することで、List
の{}
で囲まれたViewがすべてスケルトン適用されます。
viewModel.isFirstLoading
は、リアルデータが取得できた時にfalse
になるようにしているので、データが取れたら、自動的にスケルトンが解除されます。
以上となります。
List形式のViewで、ロード中のスケルトンを実装したい方の参考になれば幸いです。
参考にさせていただいた記事