37ece249800351448885f31aabf04bcb
SwiftUI 常用组件之 可刷新滚动视图View支持scrollview list

实战需求

SwiftUI 常用组件之 可刷新滚动视图View

本文价值与收获

看完本文后,您将能够作出下面的界面

SwiftUI 常用组件之 可刷新滚动视图View

SwiftUI 常用组件之 可刷新滚动视图View


基础知识

基本用法

它使用ScrollView作为内容的包装器

RefreshableScrollView(refreshing: $refresh, action: {
    // add your code here
    // remmber to set the refresh to false
    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
        self.refresh = false
    }
}) {
    ForEach(0..<20){ index in
        Text("Item \(index)")
    }
}

与列表一起使用

在某些情况下,您希望使用列表视图而不是滚动视图。您可以将滚动类型更改为.list。滚动类型有两种。

.列表
.scrollView:这是一个默认值

RefreshableScrollView(refreshing: $refresh, scrollType: .list
    action: { self.load() }
) {
    ForEach(0..<20){ index in
        Text("Item \(index)")
    }
}

自定义活动视图

RefreshableScrollView(refreshing: $refresh,
                      activityView: AnyView(InfiniteProgressView().frame(width: 20, height: 20, alignment: .center)),
                      action: { self.load() }) {
    ForEach(0..<20){ index in
        Text("Item \(index)")
    }
}

自定义拉视图

RefreshableScrollView(refreshing: $refresh,
                      activityView: AnyView(InfiniteProgressView().frame(width: 20, height: 20, alignment: .center)),
                      pullView: { height, rotation, loading, frozen in
                        let image = Image(systemName: "shift") // If not loading, show the arrow
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: height * 0.25, height: height * 0.25).fixedSize()
                            .padding(height * 0.375)
                            .rotationEffect(rotation)
                            .offset(y: -height + (loading && frozen ? +height : 0.0))
                        return AnyView(image) },
                      action: { self.load() }
) {
    ForEach(0..<20){ index in
        Text("Item \(index)")
    }
}

实战代码

主界面

import SwiftUI

struct ContentView: View {
    @State var refresh = false
    var body: some View {
        RefreshableScrollView(refreshing: $refresh, action: {
            // add your code here
            // remmber to set the refresh to false
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                self.refresh = false
            }
        }) {
            ForEach(0..<20){ index in
                Text("Item \(index)")
            }
        }
    }
}

主要组件

```
import SwiftUI

public struct RefreshableScrollView: View {
@State private var previousScrollOffset: CGFloat = 0
@State private var scrollOffset: CGFloat = 0
@State private var frozen: Bool = false
@State private var rotation: Angle = .degrees(0)

var threshold: CGFloat = 80
@Binding var refreshing: Bool
let content: Content
public typealias Action = ()->Void
let action: Action?
@State var fixedMinY: CGFloat = 0

public init(refreshing: Binding<Bool>, action: Action? = nil, @ViewBuilder content: () -> Content) {
    self.content = content()
    self.action = action
    self._refreshing = refreshing
}

public var body: some View {
    VStack {
        ScrollView {
            ZStack(alignment: .top) {
                MovingView()

                VStack {
                    self.content
                }
                .alignmentGuide(.top, computeValue: { d in (self.refreshing && self.frozen) ? -self.threshold : 0.0 })
                // add an animation when a view is going up only
                .animation(.default, value: self.refreshing)

                SymbolView(height: self.threshold,
                           refreshing: self.refreshing,
                           frozen: self.frozen,
                           rotation: self.rotation,
                           offset: self.scrollOffset)

            }
        }
        .background(FixedView())
        .onPreferenceChange(RefreshableKeyTypes.PrefKey.self) { values in
            self.refreshLogic(values: values)
        }
    }
}

func refreshLogic(values: [RefreshableKeyTypes.PrefData]) {
    DispatchQueue.main.async {
        // Calculate scroll offset
        let movingBounds = values.first { $0.vType == .movingView }?.bounds ?? .zero
top Created with Sketch.