Skip to content

Instantly share code, notes, and snippets.

@niaeashes
Created March 4, 2022 16:16
Show Gist options
  • Save niaeashes/a60819dfd466961d8621fb0af2129d3d to your computer and use it in GitHub Desktop.
Save niaeashes/a60819dfd466961d8621fb0af2129d3d to your computer and use it in GitHub Desktop.
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollView {
LazyVStack {
ForEach(0..<10000, id: \.self) { i in
Text("No.\(i)")
.padding()
}
}
}
.refreshable { proxy in
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { proxy.complete() }
}
}
}
struct RefreshControl: View {
@Environment(\.isEnabled) var isEnabled
var body: some View {
ProgressView()
.progressViewStyle(.circular)
.frame(height: isEnabled ? 40 : 0)
.offset(y: isEnabled ? 0 : -20)
}
}
struct RefreshableModifier: ViewModifier {
let space: CoordinateSpace
let receiver: (Proxy) -> Void
@State var isNowRefresh = false
@State var refreshOpacity: Double = 0
func body(content: Content) -> some View {
ZStack(alignment: .top) {
VStack {
RefreshControl()
.disabled(!isNowRefresh)
.opacity(isNowRefresh ? 1.0 : refreshOpacity)
content
}
GeometryReader { geometry in
let offset = -geometry.frame(in: space).minY
Color.clear
.preference(key: OffsetKey.self, value: offset)
.onPreferenceChange(OffsetKey.self) {
refreshOpacity = min(1, max(0, -$0 / 40))
if $0 < -80 {
withAnimation(.spring().speed(2)) { isNowRefresh = true }
receiver(Proxy($isNowRefresh))
}
}
}
.frame(height: 0)
}
}
struct OffsetKey: PreferenceKey {
static var defaultValue: CGFloat = .zero
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
class Proxy {
let binding: Binding<Bool>
init(_ binding: Binding<Bool>) {
self.binding = binding
}
func complete() {
withAnimation { binding.wrappedValue = false }
}
}
}
extension ScrollView {
func refreshable(receiver: @escaping (RefreshableModifier.Proxy) -> Void) -> some View {
let id = UUID()
let space = CoordinateSpace.named(id)
return ScrollView<ModifiedContent<Content, RefreshableModifier>> {
content
.modifier(RefreshableModifier(space: space, receiver: receiver))
}
.coordinateSpace(name: id)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment