怠慢プログラマーの備忘録

怠慢でナマケモノなプログラマーの備忘録です。

【SwiftUI】ScrollViewで列挙したリストにpull to refreshの挙動を追加する[備忘録]

SwiftUIのScrollViewにはPull to refreshのようなコンポーネントはUIScrollViewのようには付属されていないので自作した時のメモです。

IndicatorView

struct IndicatorView: View {
    var body: some View {
        VStack {
            Spacer()
            HStack(spacing: 0) {
                Spacer()
                LottieAnimationView(name: "loading")
                    .frame(width: 100, height: 100, alignment: .center)
                Spacer()
            }
            Spacer()
        }
        .background(Color(.white))
        .opacity(0.8)
    }
}

※LottieAnimationViewはLottieのAnimationViewを実装したUIViewRepresentableです。

HiddenModifier

struct HiddenModifier: ViewModifier {
    let hidden: Bool
    func body(content: Content) -> some View {
        VStack {
            if !hidden {
                content
            }
        }
    }
}

extension View {
    func hidden(_ isHidden: Bool) -> some View {
        ModifiedContent(content: self, modifier: HiddenModifier(hidden: isHidden))
    }
}

ContentView

struct DiaryListView: View {
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        ZStack {
            ScrollView {
                GeometryReader { proxy -> AnyView in
                    let frame = proxy.frame(in: .global)
                    if frame.origin.y > 180 && !viewModel.outputs.isLoading {
                        viewModel.inputs.reload.send(true)
                    }
                    return AnyView(EmptyView())
                }

                ForEach(viewModel.outputs.items, id: \.id) { item in
                    Text(item.name)
                }
                .padding(.vertical, 8)
                .padding(.horizontal, 24)
            }
            IndicatorView()
                .frame(width: UIScreen.main.bounds.width, height: coordinator.hostingController?.view.frame.height ?? UIScreen.main.bounds.height)
                .hidden(!viewModel.outputs.isLoading)
        }
    }
}

ViewModelのisLoadingが@Publishedになっているのでtrue/falseが切り替えられると.hidden()が切り替わります。

SwiftUI 徹底入門

SwiftUI 徹底入門