Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/gh123man/swiftui-refresher
A native, customizable SwiftUI refresh control
https://github.com/gh123man/swiftui-refresher
ios swift swiftui
Last synced: 2 days ago
JSON representation
A native, customizable SwiftUI refresh control
- Host: GitHub
- URL: https://github.com/gh123man/swiftui-refresher
- Owner: gh123man
- License: mit
- Created: 2022-04-18T02:02:32.000Z (over 2 years ago)
- Default Branch: master
- Last Pushed: 2024-06-20T03:17:02.000Z (6 months ago)
- Last Synced: 2024-12-15T17:11:48.701Z (9 days ago)
- Topics: ios, swift, swiftui
- Language: Swift
- Homepage:
- Size: 10.3 MB
- Stars: 131
- Watchers: 4
- Forks: 13
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Refresher
A customizable, native SwiftUI refresh control for iOS 14+
## Why?
- the native SwiftUI refresh control only works on iOS 15+
- the native UIKit refresh control works with ugly wrappers, but has buggy behavior with navigation views
- I needed a refresh control that could accomodate an overlay (such as appearing on top of a static image)
- This one is very customizable## See it in action
If you want to see it in a real app, check out [dateit](https://apps.apple.com/us/app/dateit/id1610780514)Also works well with [ScrollViewLoader](https://github.com/gh123man/ScrollViewLoader)
## Usage
First add the package to your project.```swift
import Refresherstruct DetailsView: View {
@State var refreshed = 0var body: some View {
ScrollView {
Text("Details!")
Text("Refreshed: \(refreshed)")
}
.refresher { // Called when pulled to refresh
await Task.sleep(seconds: 2)
refreshed += 1
}
}
}
```## Features
- `async`/`await` compatible - even on iOS 14
- completion callback also supported for `DispatchQueue` operations
- `.default` and `.system` styles (see below for details)
- customizable refresh spinner (see below for example)## Examples and usage
See: [Examples](/Examples/) for a full sample project with multiple implementations
### Navigation view
![Navigation](/images/1.gif)
`Refresher` plays nice with both Navigation views and navigation subviews.
![Subview](/images/3.gif)
### Detail view with overlay
`Refresher` supports an overlay mode to show a refresh indicator over fixed position content
`.refresher(overlay: true)`
![Overlay](/images/2.gif)
### System style
`Refresher`'s default animation is designed to be more flexible than the system animation style. If you want `Refresher` to behave more like they system refresh control, you can change the style:```swift
.refresher(style: .system) { done in
```![System](/images/5.gif)
## Customization
Refresher can take a custom spinner view. Your custom view will get a binding instances of the refresher state that contains useful properties for managing animations and translations. Here is a custom spinner that shows an emoji:
```swift
public struct EmojiRefreshView: View {
@Binding var state: RefresherState
@State private var angle: Double = 0.0
@State private var isAnimating = false
var foreverAnimation: Animation {
Animation.linear(duration: 1.0)
.repeatForever(autoreverses: false)
}
public var body: some View {
VStack {
switch state.mode {
case .notRefreshing:
Text("ðĪŠ")
.onAppear {
isAnimating = false
}
case .pulling:
Text("ðŊ")
.rotationEffect(.degrees(360 * state.dragPosition))
case .refreshing:
Text("ð")
.rotationEffect(.degrees(self.isAnimating ? 360.0 : 0.0))
.onAppear {
withAnimation(foreverAnimation) {
isAnimating = true
}
}
}
}
.scaleEffect(2)
}
}
```Add the custom refresherView:
```swift
.refresher(refreshView: EmojiRefreshView.init ) { done in
```![Custom](/images/4.gif)
## Completion handler
If you prefer to call a completion to stop the refresher:
```swift
.refresher(style: .system) { done in
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
refreshed += 1
done() // Call done to stop the refresher
}
}
```