Skip to content

Commit b898aef

Browse files
committed
Improve digitalCrown experience
1 parent bbe61ad commit b898aef

File tree

6 files changed

+66
-43
lines changed

6 files changed

+66
-43
lines changed

Sources/SwiftUIPager/Page.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public class Page: ObservableObject {
5454

5555
var isInfinite = false
5656

57+
var lastDigitalCrownPageOffset: CGFloat = 0
58+
5759
/// Initializes a new instance
5860
///
5961
/// - Parameter page: Current page index

Sources/SwiftUIPager/Pager+Buildable.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,6 @@ extension Pager: Buildable {
7070

7171
#if !os(tvOS)
7272

73-
/// Sets the explicit animation to be used. Defaults to `.standard`
74-
///
75-
/// - Parameter value: animation to use while dragging and to page
76-
///
77-
/// - Warning: `spring` animations don't work well. Avoid high responses while dragging as the animation should be short
78-
public func draggingAnimation(_ value: DraggingAnimation) -> Self {
79-
mutating(keyPath: \.draggingAnimation, value: value)
80-
}
81-
8273
/// User can only swipe forward so in one direction
8374
///
8475
/// - Parameter enabled: by default dragForwardOnly is disables so pages can be scrolled in both directions,
@@ -169,8 +160,19 @@ extension Pager: Buildable {
169160
mutating(keyPath: \.onDraggingEnded, value: callback)
170161
}
171162

172-
#else
173-
163+
#endif
164+
165+
/// Adds a callback to react when _iWatch Digital Crown_ is rotated
166+
///
167+
/// - Parameter callback: block to be called when dragging begins
168+
@available(iOS, unavailable)
169+
@available(macOS, unavailable)
170+
@available(tvOS, unavailable)
171+
@available(watchOS 7.0, *)
172+
public func onDigitalCrownRotated(_ callback: ((Double) -> Void)?) -> Self {
173+
mutating(keyPath: \.onDigitalCrownRotated, value: callback)
174+
}
175+
174176
/// Sets the explicit animation to be used. Defaults to `.standard`
175177
///
176178
/// - Parameter value: animation to use while dragging and to page
@@ -180,8 +182,6 @@ extension Pager: Buildable {
180182
mutating(keyPath: \.draggingAnimation, value: value)
181183
}
182184

183-
#endif
184-
185185
/// Changes the a the alignment of the pages relative to their container
186186
///
187187
/// - Parameter value: alignment of the pages inside the scroll

Sources/SwiftUIPager/Pager.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,18 @@ public struct Pager<Element, ID, PageView>: View where PageView: View, Element:
153153
/// Callback invoked when a new page is set
154154
var onPageChanged: ((Int) -> Void)?
155155

156-
/// Callback for when dragging begins
156+
/// Callback for a dragging began event
157157
var onDraggingBegan: (() -> Void)?
158158

159-
/// Callback for when dragging changes
159+
/// Callback for a dragging changed event
160160
var onDraggingChanged: ((Double) -> Void)?
161161

162-
/// Callback for when dragging ends
162+
/// Callback for a dragging ended event
163163
var onDraggingEnded: (() -> Void)?
164164

165+
/// Callback for a digital crown rotated event
166+
var onDigitalCrownRotated: ((Double) -> Void)?
167+
165168
/*** State and Binding properties ***/
166169

167170
let pagerModel: Page
@@ -227,6 +230,12 @@ public struct Pager<Element, ID, PageView>: View where PageView: View, Element:
227230
pagerContent = pagerContent.draggingAnimation(draggingAnimation)
228231
#endif
229232

233+
#if os(watchOS)
234+
if #available(watchOS 7.0, *) {
235+
pagerContent = pagerContent.onDigitalCrownRotated(onDigitalCrownRotated)
236+
}
237+
#endif
238+
230239
pagerContent = allowsMultiplePagination ? pagerContent.multiplePagination() : pagerContent
231240
pagerContent = isHorizontal ? pagerContent.horizontal(horizontalSwipeDirection) : pagerContent.vertical(verticalSwipeDirection)
232241

Sources/SwiftUIPager/PagerContent+Buildable.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,6 @@ extension Pager.PagerContent: Buildable {
7272

7373
#if !os(tvOS)
7474

75-
/// Sets the explicit animation to be used. Defaults to `.standard`
76-
///
77-
/// - Parameter value: animation to use while dragging and to page
78-
///
79-
/// - Warning: `spring` animations don't work well. Avoid high responses while dragging as the animation should be short
80-
func draggingAnimation(_ value: DraggingAnimation) -> Self {
81-
mutating(keyPath: \.draggingAnimation, value: value)
82-
}
83-
8475
/// User can only swipe forward so in one direction
8576
///
8677
/// - Parameter enabled: by default dragForwardOnly is disables so pages can be scrolled in both directions,
@@ -166,19 +157,28 @@ extension Pager.PagerContent: Buildable {
166157
mutating(keyPath: \.onDraggingEnded, value: callback)
167158
}
168159

169-
#else
160+
#endif
161+
162+
/// Adds a callback to react when _iWatch Digital Crown_ is rotated
163+
///
164+
/// - Parameter callback: block to be called when dragging begins
165+
@available(iOS, unavailable)
166+
@available(macOS, unavailable)
167+
@available(tvOS, unavailable)
168+
@available(watchOS 7.0, *)
169+
func onDigitalCrownRotated(_ callback: ((Double) -> Void)?) -> Self {
170+
mutating(keyPath: \.onDigitalCrownRotated, value: callback)
171+
}
170172

171173
/// Sets the explicit animation to be used. Defaults to `.standard`
172174
///
173175
/// - Parameter value: animation to use while dragging and to page
174176
///
175177
/// - Warning: `spring` animations don't work well. Avoid high responses while dragging as the animation should be short
176-
public func draggingAnimation(_ value: DraggingAnimation) -> Self {
178+
func draggingAnimation(_ value: DraggingAnimation) -> Self {
177179
mutating(keyPath: \.draggingAnimation, value: value)
178180
}
179181

180-
#endif
181-
182182
/// Changes the a the alignment of the pages relative to their container
183183
///
184184
/// - Parameter value: alignment of the pages inside the scroll

Sources/SwiftUIPager/PagerContent.swift

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,18 @@ extension Pager {
131131
/// Callback invoked when a new page is set
132132
var onPageChanged: ((Int) -> Void)?
133133

134-
/// Callback for when dragging begins
134+
/// Callback for a dragging began event
135135
var onDraggingBegan: (() -> Void)?
136136

137-
/// Callback for when dragging changes
137+
/// Callback for a dragging changed event
138138
var onDraggingChanged: ((Double) -> Void)?
139139

140-
/// Callback for when dragging ends
140+
/// Callback for a dragging ended event
141141
var onDraggingEnded: (() -> Void)?
142142

143+
/// Callback for a digital crown rotated event
144+
var onDigitalCrownRotated: ((Double) -> Void)?
145+
143146
/*** State and Binding properties ***/
144147

145148
/// Page index
@@ -155,9 +158,6 @@ extension Pager {
155158
/// Digital Crown offset
156159
@State var digitalCrownPageOffset: CGFloat = 0
157160

158-
/// Digital Crown offset
159-
@State var lastDigitalCrownPageOffset: CGFloat = 0
160-
161161
#endif
162162

163163
/// Initializes a new `Pager`.
@@ -246,7 +246,7 @@ extension Pager {
246246
#endif
247247

248248
#if os(watchOS)
249-
if #available(watchOSApplicationExtension 7.0, *) {
249+
if #available(watchOS 7.0, *) {
250250
resultView = resultView
251251
.focusable()
252252
.digitalCrownRotation(
@@ -257,12 +257,21 @@ extension Pager {
257257
sensitivity: .low
258258
)
259259
.onChange(of: digitalCrownPageOffset) { newValue in
260-
print(newValue)
261-
let increment = min(1, max(-1, Int(newValue - lastDigitalCrownPageOffset)))
262-
guard abs(increment) > 0 else { return }
263-
lastDigitalCrownPageOffset = newValue
264-
withAnimation {
265-
pagerModel.update(.move(increment: increment))
260+
let pageIncrement = min(1, max(-1, Int(newValue - pagerModel.lastDigitalCrownPageOffset)))
261+
let offset = (newValue - pagerModel.lastDigitalCrownPageOffset) - CGFloat(pageIncrement)
262+
onDigitalCrownRotated?(newValue * pageDistance)
263+
let animation = self.draggingAnimation.animation ?? .default
264+
guard abs(pageIncrement) > 0 else {
265+
withAnimation(animation) {
266+
pagerModel.draggingOffset = -offset * pageDistance
267+
pagerModel.objectWillChange.send()
268+
}
269+
return
270+
}
271+
withAnimation(animation) {
272+
pagerModel.lastDigitalCrownPageOffset = newValue - offset
273+
pagerModel.draggingOffset = -offset
274+
pagerModel.update(.move(increment: pageIncrement))
266275
}
267276
}
268277
.eraseToAny()
@@ -284,10 +293,12 @@ extension Pager.PagerContent {
284293
let animation = self.draggingAnimation.animation ?? .default
285294
switch (command, isHorizontal) {
286295
case (.left, true):
296+
guard !dragForwardOnly else { return }
287297
withAnimation(animation) { self.pagerModel.update(.previous) }
288298
case (.right, true):
289299
withAnimation(animation) { self.pagerModel.update(.next) }
290300
case (.up, false):
301+
guard !dragForwardOnly else { return }
291302
withAnimation(animation) { self.pagerModel.update(.previous) }
292303
case (.down, false):
293304
withAnimation(animation) { self.pagerModel.update(.next) }

release_description.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
### Features
2-
- #254 New modifier `dragForwardOnly`
2+
- #254 New modifier `dragForwardOnly`
3+
- #265 Support for _watchOS_ `digitalCrownRotation`

0 commit comments

Comments
 (0)