Last active
January 12, 2022 06:30
-
-
Save Iron-Ham/dd6945ca33ec5b6adfc80f14a826233e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
public struct RefreshableScrollView<Content, Value>: View where Content: View { | |
private let content: () -> Content | |
private let action: () async -> Value | |
init(@ViewBuilder content: @escaping () -> Content, action: @escaping () async -> Value) { | |
self.content = content | |
self.action = action | |
} | |
@State var startY: Double = Double.greatestFiniteMagnitude | |
@State var isLoading = false | |
@State var value: Value? | |
public var body: some View { | |
ScrollView { | |
GeometryReader { geometry in | |
HStack { | |
Spacer() | |
ProgressView() | |
.opacity(calculate(offset: geometry.frame(in: .global).minY)) | |
.padding(.top, -30) | |
.transition(.move(edge: .top)) | |
.onChange(of: geometry.frame(in: .global), perform: { frame in | |
if !hasReachedThreshold(frame.minY) && isLoading && value != nil { | |
isLoading = false | |
return | |
} | |
guard hasReachedThreshold(frame.minY), !isLoading else { return } | |
self.isLoading = true | |
let feedback = UIImpactFeedbackGenerator(style: .light) | |
feedback.prepare() | |
feedback.impactOccurred() | |
Task.init(priority: .userInitiated, operation: { | |
self.value = await action() | |
}) | |
}) | |
Spacer() | |
} | |
.onAppear { | |
startY = geometry.frame(in: .global).minY | |
} | |
} | |
content() | |
} | |
} | |
private func hasReachedThreshold(_ offset: CGFloat) -> Bool { | |
calculate(offset: offset) == 1 | |
} | |
private func calculate(offset: CGFloat) -> CGFloat { | |
min((offset - startY) / 30 + 1, 1.0) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment