ソースコード
Viewのレイアウトは参考文献より引用。
import SwiftUI
// trueならばViewを表示、falseならばEmptyView()
extension View {
@ViewBuilder func isHidden(_ hidden: Bool) -> some View {
if hidden {
EmptyView()
} else {
self
}
}
}
struct LoadingIndicatorView: View {
let isLoading: Bool
@State private var isAnimating = false
private let animation = Animation.linear(duration: 1).repeatForever(autoreverses: false)
init(_ isLoading:Bool){
self.isLoading = isLoading
}
var body: some View {
GeometryReader { geometry in
ZStack {
// ①Loading中に画面をタップできないようにするためのほぼ透明なLayer
Color(.black)
.opacity(0.01)
.frame(width: geometry.size.width, height: geometry.size.height)
.edgesIgnoringSafeArea(.all)
.disabled(self.isLoading)
Circle()
.trim(from: 0, to: 0.6)
.stroke(AngularGradient(gradient: Gradient(colors: [.gray, .white]), center: .center),
style: StrokeStyle(
lineWidth: 8,
lineCap: .round,
dash: [0.1, 16],
dashPhase: 8))
.frame(width: 48, height: 48)
.rotationEffect(.degrees(self.isAnimating ? 360 : 0))
.onAppear() {
withAnimation(Animation.linear(duration: 1).repeatForever(autoreverses: false)) {
self.isAnimating = true
}
}
.onDisappear() {
self.isAnimating = false
}
}
.isHidden(!self.isLoading)
}
}
}
struct LoadingIndicatorView_Previews: PreviewProvider {
static var previews: some View {
LoadingIndicatorView(true)
}
}
補足
View表示の切り替えはextension
で拡張したViewプロパティを使用。(標準Viewプロパティ.hidden()
は引数を持たないため表示・非表示の切り替えが煩わしい)
最適化を考慮し、self.hidden()
の代わりにEmptyView()
を返している。
参考文献
この記事は以下の情報を参考にして執筆しました。