Skip to content

Commit 974578e

Browse files
committed
cleanup, docs, animation splitting
1 parent b479550 commit 974578e

File tree

5 files changed

+39
-26
lines changed

5 files changed

+39
-26
lines changed

Diff for: Examples/ContentView.swift

+6-10
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,14 @@ struct ContentView: View {
4949
struct DetailsView: View {
5050
@State var refreshed = 0
5151
var style: Style
52-
var useImage = true
52+
@State var useImage = true
5353
var body: some View {
5454
ScrollView {
5555
VStack {
5656
if useImage {
5757
Image("photo")
5858
.resizable()
59-
.aspectRatio(contentMode: .fit)
59+
.aspectRatio(contentMode: .fill)
6060
}
6161
Text("Details!")
6262
Text("Refreshed: \(refreshed)")
@@ -113,7 +113,7 @@ struct DetailsCustom: View {
113113
}
114114
}
115115
.refresher(refreshView: EmojiRefreshView.init ) { done in
116-
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
116+
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1500)) {
117117
refreshed += 1
118118
done()
119119
}
@@ -124,23 +124,19 @@ struct DetailsCustom: View {
124124

125125
public struct EmojiRefreshView: View {
126126
@Binding var state: RefresherState
127-
@State var angle: Double = 0.0
128-
@State var isAnimating = false
127+
@State private var angle: Double = 0.0
128+
@State private var isAnimating = false
129129

130130
var foreverAnimation: Animation {
131131
Animation.linear(duration: 1.0)
132132
.repeatForever(autoreverses: false)
133133
}
134134

135-
public init(state: Binding<RefresherState>) {
136-
self._state = state
137-
}
138-
139135
public var body: some View {
140136
VStack {
141137
switch state.mode {
142138
case .notRefreshing:
143-
Text("😂")
139+
Text("🤪")
144140
.onAppear {
145141
isAnimating = false
146142
}

Diff for: README.md

+3-7
Original file line numberDiff line numberDiff line change
@@ -68,23 +68,19 @@ Refresher can take a custom spinner view. Your custom view will get a binding in
6868
```swift
6969
public struct EmojiRefreshView: View {
7070
@Binding var state: RefresherState
71-
@State var angle: Double = 0.0
72-
@State var isAnimating = false
71+
@State private var angle: Double = 0.0
72+
@State private var isAnimating = false
7373

7474
var foreverAnimation: Animation {
7575
Animation.linear(duration: 1.0)
7676
.repeatForever(autoreverses: false)
7777
}
7878

79-
public init(state: Binding<RefresherState>) {
80-
self._state = state
81-
}
82-
8379
public var body: some View {
8480
VStack {
8581
switch state.mode {
8682
case .notRefreshing:
87-
Text("😂")
83+
Text("🤪")
8884
.onAppear {
8985
isAnimating = false
9086
}

Diff for: Sources/Refresher/RefreshSpinner.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ public struct SystemStyleRefreshSpinner<RefreshView: View>: View {
4646
refreshView
4747
.frame(maxWidth: .infinity)
4848
.position(x: geometry.size.width / 2, y: -position + refreshHoldPoint)
49-
.opacity(state.mode == .refreshing ? 1 : normalize(from: opacityClipPoint, to: 1, by: state.dragPosition))
49+
.opacity(state.modeAnimated == .refreshing ? 1 : normalize(from: opacityClipPoint, to: 1, by: state.dragPosition))
50+
.animation(.easeInOut(duration: 0.2), value: state.modeAnimated == .notRefreshing)
5051
}
5152
}
5253
}

Diff for: Sources/Refresher/Refresher.swift

+28-8
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@ import SwiftUI
44
public typealias RefreshAction = (_ completion: @escaping () -> ()) -> ()
55

66
public enum Style {
7+
8+
/// Spinner pulls down and centers on a padding view above the scrollview
79
case `default`
10+
11+
/// Mimic the system refresh controller as close as possible
812
case system
13+
14+
/// Overlay the spinner onto the cotained view - good for static images
915
case overlay
1016
}
1117

@@ -16,8 +22,17 @@ public enum RefreshMode {
1622
}
1723

1824
public struct RefresherState {
25+
26+
/// Updated without animation - NOTE: Both modes are always updated in sequence (this one is first)
1927
public var mode: RefreshMode = .notRefreshing
28+
29+
/// Updated with animation (this one is second)
30+
public var modeAnimated: RefreshMode = .notRefreshing
31+
32+
/// Value from 0 - 1. 0 is resting state, 1 is refresh trigger point - use this value for custom translations
2033
public var dragPosition: CGFloat = 0
34+
35+
/// the configuration style - useful if you want your custom spinner to change behavior based on the style
2136
public var style: Style = .default
2237
}
2338

@@ -57,7 +72,7 @@ public struct RefreshableScrollView<Content: View, RefreshView: View>: View {
5772
private var refreshBanner: AnyView? {
5873
switch state.style {
5974
case .default, .system:
60-
if case .refreshing = state.mode {
75+
if case .refreshing = state.modeAnimated {
6176
return AnyView(Color.clear.frame(height: headerShimMaxHeight * (1 - state.dragPosition)))
6277
}
6378
case .overlay:
@@ -69,7 +84,7 @@ public struct RefreshableScrollView<Content: View, RefreshView: View>: View {
6984

7085
private var refershSpinner: AnyView? {
7186
return state.style == .default || state.style == .overlay
72-
? AnyView(RefreshSpinnerView(mode: state.mode,
87+
? AnyView(RefreshSpinnerView(mode: state.modeAnimated,
7388
stopPoint: spinnerStopPoint,
7489
refreshHoldPoint: headerShimMaxHeight / 2,
7590
refreshView: refreshView($state),
@@ -134,25 +149,30 @@ public struct RefreshableScrollView<Content: View, RefreshView: View>: View {
134149
if !canRefresh { return }
135150

136151
guard distance > 0 else {
137-
state.mode = .notRefreshing
152+
set(mode: .notRefreshing)
138153
return
139154
}
140155

141156
if distance >= refreshAt {
142157
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
143-
state.mode = .refreshing
158+
set(mode: .refreshing)
144159
canRefresh = false
145160

146161
refreshAction {
147162
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
148-
withAnimation {
149-
state.mode = .notRefreshing
150-
}
163+
set(mode: .notRefreshing)
151164
}
152165
}
153166

154167
} else if distance > 0 {
155-
state.mode = .pulling
168+
set(mode: .pulling)
169+
}
170+
}
171+
172+
func set(mode: RefreshMode) {
173+
state.mode = mode
174+
withAnimation {
175+
state.modeAnimated = mode
156176
}
157177
}
158178
}

Diff for: images/4.gif

-892 KB
Loading

0 commit comments

Comments
 (0)