Skip to content

Instantly share code, notes, and snippets.

@antonyalkmim
Created March 6, 2024 18:10
Show Gist options
  • Save antonyalkmim/fbe69aa738c7af74b9035586ef427f60 to your computer and use it in GitHub Desktop.
Save antonyalkmim/fbe69aa738c7af74b9035586ef427f60 to your computer and use it in GitHub Desktop.
SwiftUI RefreshableCompat iOS13
import SwiftUI
private struct RefreshableCompat: ViewModifier {
let onRefresh: () async -> Void
@State private var isRefreshing = false
private let threshold: CGFloat = 50.0
func body(content: Content) -> some View {
GeometryReader { proxy in
ZStack(alignment: .top) {
ScrollView {
content
.background(Color.white)
.anchorPreference(key: OffsetPreferenceKey.self, value: .top) { proxy[$0].y }
}
.frame(maxWidth: .infinity)
.padding(.top, isRefreshing ? threshold : 0)
.zIndex(2)
ProgressView()
.zIndex(1)
}
.onPreferenceChange(OffsetPreferenceKey.self) { offset in
if offset > threshold, !isRefreshing {
isRefreshing = true
refresh()
}
}
}
}
private func refresh() {
Task {
await onRefresh()
isRefreshing = false
}
}
}
private struct OffsetPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
public extension View {
@_disfavoredOverload
@ViewBuilder
func refreshable(_ perform: @escaping () async -> Void) -> some View {
modifier(RefreshableCompat(onRefresh: perform))
}
}
#if DEBUG && !TESTING
private struct DummyView: View {
@State private var items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6"]
var body: some View {
ForEach(items, id: \.self) { item in
JadeText(item)
.frame(maxWidth: .infinity)
}
.refreshable {
try? await Task.sleep(nanoseconds: 1_000_000_000 * 3)
items.shuffle()
}
}
}
struct RefreshableCompat_Previews: PreviewProvider {
static var previews: some View {
DummyView()
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment