APIから情報をフェッチしている間、空白の画面を表示することは避けたいものです。ローディングインジケータを表示するべきでしょう。あるいは、ビューにSwiftUIのプレースホルダ―オーバーレイを設置することもできます。
この記事では .redacted
を使って簡単にプレースホルダ―オーバーレイを追加する方法、ビューのプレースホルダーオーバーレイ表示ステータスを監視する方法、プレースホルダーオーバーレイにアニメーションを追加する方法について述べます。
.redacted
と .unredacted()
ビューモディファイア
.redacted
モディファイアは、データのダウンロードを行っている最中には非表示にしたいビュー要素に追加することができます。
.redacted(reason: (weatherInformation == nil) ? .placeholder : [])
reason
が .placeholder
と等しい場合、オーバーレイがビューに追加されます。また、reason
変数が空配列と等しい場合は表示されるプレースホルダーオーバーレイはありません。
また、テキストビュー要素用に何らかのデフォルト値を指定する必要もあります。それにより、生成するプレースホルダーのサイズをSwiftUIが把握します。例えば
Text(weatherInformation?.weatherDescription ?? "気象情報をダウンロードしています...")
常にスクリーンに表示しておきたいエレメントに対しては、.unredacted()
ビューモディファイヤーを追加してください。
プレイスホルダーを表示するかどうかをチェックするための拡張を追加する
コードに拡張を追加することもできます:
extension View {
@ViewBuilder
func redacted(showPlaceholder: Bool) -> some View {
if showPlaceholder {
self
.redacted(reason: .placeholder)
} else {
self
.unredacted()
}
}
}
よって showPlaceholder
変数によって容易にプレイスホルダーを表示するか隠すかを決定できます
.redacted(showPlaceholder: (weatherInformation == nil))
SwiftUIビューへのビュー修飾子の適用
まず、基本的な気象情報を表示するSwiftUIビューがあります。
struct WeatherView: View {
@Binding var weatherInformation: Weather?
@Environment(\.redactionReasons) var redactions
var body: some View {
HStack {
Image("sunnyImage")
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
.unredacted()
VStack(alignment: .leading) {
Text(weatherInformation?.status ?? "ダウンロードしています...")
.font(.largeTitle)
.padding(.vertical, 3)
Text(weatherInformation?.weatherDescription ?? "気象情報をダウンロードしています...")
.padding(.vertical, 3)
Label(weatherInformation?.degrees ?? "...", systemImage: "thermometer.sun")
.font(.title2)
.padding(.vertical, 3)
}
}
}
}
1> このビューのコードでは、SwiftUIが自動的にオーバーレイを生成することができるように、テキストとラベルのデフォルト値を指定する必要があります。
2> 上記の例の Image
オブジェクトには、修飾子 .unredacted()
があります。これは、ユーザーに常に表示されることを意味します。
コンテンツ閲覧に作成されたばかりの WeatherView
でそれを用いることができます。
Section {
WeatherView(weatherInformation: $weatherInformation)
.redacted(reason: (weatherInformation == nil) ? .placeholder : [])
//.redacted(showPlaceholder: (weatherInformation == nil))
}
WeatherView
内の画像以外の閲覧対象が表示されるのは weatherInformation == nil
条件が当てはまらない場合のみです。
そしてこの例では、weatherInformation
を設定することができます。
.onAppear(perform: {
DispatchQueue.main.asyncAfter(deadline: .now() + 3.5) {
self.weatherInformation = Weather(status: "晴れ", weatherDescription: "今日の東京の天気は晴れです。", degrees: "23度")
}
})
プレースホルダーオーバーレイのモニター
@Environment
変数 \.redactionReasons
を使って WeatherView
に加えられたプレースホルダーオーバーレイの有無をモニターすることができます。
@Environment(\.redactionReasons) var redactions
if self.redactions.isEmpty {
Button(action: {}, label: {
Text("天気予報のビュー表示")
})
}
データのダウンロード中にアニメーションを追加する
データのダウンロード中にアニメーションを追加することもできます。例えば、プレースホルダーのオーバーレイをぼかすアニメーションを追加したり、不透明度を変更したりすることができます。
ここでは、ビューにアニメーションを追加するビューのモディファイア struct
を作成する必要があります。これはビューの不透明度を1.0から0.3に変更して繰り返す例の1つです。
struct EaseInOutAnimation: ViewModifier {
@State private var contentOpacity = 1.0
func body(content: Content) -> some View {
content
.opacity(contentOpacity)
.animation(Animation
.easeInOut(duration: 2)
.repeatForever(autoreverses: true))
.onAppear { contentOpacity = 0.3 }
}
}
この EaseInOutAnimation
ビュー修飾子をビュー拡張機能の編集済み修飾子に適用することができます:
extension View {
@ViewBuilder
func redacted(showPlaceholder: Bool) -> some View {
if showPlaceholder {
self
.redacted(reason: .placeholder)
.modifier(EaseInOutAnimation())
} else {
self
.unredacted()
}
}
}
これで、もし showPlaceholder
が真であれば、プレースホルダーが表示され、 EaseInOutAnimation
アニメーションもビューに適用されます。
WeatherView と ContentView コード例を見る