Skip to content

Commit dd9d133

Browse files
authored
Merge pull request #163 from fermoya/develop
Release 1.14.0
2 parents 2c7816c + ea83d9d commit dd9d133

30 files changed

+377
-293
lines changed

.github/workflows/create-release.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ jobs:
1717
steps:
1818
- uses: actions/checkout@v2
1919

20+
- uses: maxim-lobanov/setup-xcode@v1
21+
with:
22+
xcode-version: latest-stable
23+
2024
- name: Create XCFramework
2125
id: xcframework
2226
run: ./scripts/build_xcframework.sh

Documentation/Usage.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,21 @@ Pager(...)
9898

9999
<img src="/resources/usage/item-alignment-start.gif" alt="Pages positioned at the start of the horizontal pager" height="640"/>
100100

101+
### Partial pagination
102+
103+
By default, `Pager` will reveal the neighbor items completely (100% of their relative size). If you wish to limit this _reveal ratio_, you can use `singlePatination(ratio:sensitivity)` to modify this ratio:
104+
105+
```swift
106+
Pager(...)
107+
.singlePagination(0.33, sensitivity: .custom(0.2))
108+
.preferredItemSize(CGSize(width: 300, height: 400))
109+
.itemSpacing(10)
110+
.background(Color.gray.opacity(0.2))
111+
```
112+
<img src="/resources/usage/single-pagination-ratio.gif" alt="Reveal Ratio set to a third of the page" height="640"/>
113+
114+
For more information about `sensitivity`, check out [Pagination sensitivity](#pagination-sensitivity).
115+
101116
### Multiple pagination
102117

103118
It's possible for `Pager` to swipe more than one page at a time. This is especially useful if your page size is small. Use `multiplePagination`.
@@ -112,6 +127,17 @@ Pager(...)
112127

113128
Be aware that this modifier will change the loading policy. See [Content Loading Policy](#content-loading-policy) for more information.
114129

130+
### More modifiers
131+
| **Modifier** | **Description** |
132+
|---|---|
133+
| `allowsDragging` | whether or not dragging is allowed |
134+
| `disableDragging` | disables dragging |
135+
| `bounces` | whether or not `Pager` should bounce |
136+
| `delaysTouches` | whether or not touches shoulf be delayed. Useful if nested in `ScrollView` |
137+
| `pageOffset` | allows _manual_ scroll |
138+
| `expandPageToEdges` | modifies `itemAspectRatio` so that the use up all the space available |
139+
140+
115141
## Paging Priority
116142

117143
For complex pages where a `Gesture` might be used, or any other `View` that internally holds a `Gesture` like `Button` or `NavigationLink`, you might come across issues when trying to swipe on top of certain areas within the page. For these scenarios, use `pagingPriority` to select the option that best suits your purpose. For instance, a page containing a `NavigationLink` won't be scrollable over the link area unless `pagingPrioriry(.simultaneous)` is added:
@@ -182,6 +208,23 @@ Transform your `Pager` into an endless sroll by using `loopPages`:
182208

183209
**Note**: You'll need a minimum number of elements to use this modifier based on the page size. If you need more items, use `loopPages(repeating:)` to let `Pager` know elements should be repeated in batches.
184210

211+
## Page Transitions
212+
213+
Use `pagingAnimation` to customize the _transition_ to the next page once the drag has ended. This is achieve by a block with a `DragResult`which contains:
214+
* Current page
215+
* Next page
216+
* Total shift
217+
* Velocity
218+
219+
By default, `pagingAnimation`is set to `standard`(a.k.a, `.easeOut`) for `singlePagination`and `steep`([custom bezier curve](https://cubic-bezier.com/#.2,1,.9,1)) for `multiplePagination`. If you wish to change the animation, you could do it as follows:
220+
221+
```swift
222+
Pager(...)
223+
.pagingAnimation({ currentPage, nextPage, totalShift, velocity in
224+
return PagingAnimation.custom(animation: .easeInOut)
225+
})
226+
```
227+
185228
## Events
186229

187230
Use `onPageChanged` to react to any change on the page index:
@@ -193,6 +236,8 @@ Pager(...)
193236
})
194237
```
195238

239+
You can also use `onDraggingBegan`, `onDraggingChanged` and `onDragginEnded` to keep track of the dragging.
240+
196241
## Add pages on demand
197242

198243
You can use `onPageChanged` to add new items on demand whenever the user is getting to the last page:
@@ -215,6 +260,30 @@ var body: some View {
215260
}
216261
```
217262

263+
At the same time, items can be added at the start. Notice you'll need to update the page yourself (as you're inserting new elements) to keep `Pager` focused on the right element:
264+
265+
```swift
266+
267+
@State var count: Int = -1
268+
@State var page: Int = 3
269+
@State var data = Array(0..<5)
270+
271+
Pager(page: self.$page,
272+
data: self.data,
273+
id: \.self) {
274+
self.pageView($0)
275+
}
276+
.onPageChanged({ page in
277+
guard page == 1 else { return }
278+
let newData = (1...5).map { $0 * self.count }
279+
withAnimation {
280+
self.data1.insert(contentsOf: newData, at: 0)
281+
self.page1 += 5
282+
self.count -= 1
283+
}
284+
})
285+
```
286+
218287
## Content Loading Policy
219288

220289
`Pager` recycles views by default and won't have loaded all pages in memory at once. In some scenarios, this might be counterproductive, for example, if you're trying to manually scroll from the first page to the last. For these scenarios, use `.contentLoadingPolicy` and choose among the options available.

Example/SwiftUIPagerExample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
1751176A2573D93F00D809CF /* PagerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 175117692573D93F00D809CF /* PagerModel.swift */; };
1011
17D9E0F423D4CF6700C5AE93 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D9E0F323D4CF6700C5AE93 /* AppDelegate.swift */; };
1112
17D9E0F623D4CF6700C5AE93 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D9E0F523D4CF6700C5AE93 /* SceneDelegate.swift */; };
1213
17D9E0F823D4CF6700C5AE93 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D9E0F723D4CF6700C5AE93 /* ContentView.swift */; };
@@ -20,7 +21,6 @@
2021
6B4EC8A8240D1182001E7490 /* BizarreExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B4EC8A7240D1182001E7490 /* BizarreExampleView.swift */; };
2122
6B6FAA3D24D553C8000D1539 /* PagingAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B6FAA3C24D553C8000D1539 /* PagingAnimation.swift */; };
2223
6B9C4A8A24B45F66004C06C5 /* OnDeactivateModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9C4A8924B45F66004C06C5 /* OnDeactivateModifier.swift */; };
23-
6BB1AAD324C9C9D20032B5A3 /* PagerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB1AAD224C9C9D20032B5A3 /* PagerProxy.swift */; };
2424
6BB1AAD524C9CA1C0032B5A3 /* PagerContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB1AAD424C9CA1C0032B5A3 /* PagerContent.swift */; };
2525
6BB1AAD724C9D0820032B5A3 /* Pager+Buildable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB1AAD624C9D0820032B5A3 /* Pager+Buildable.swift */; };
2626
6BC5EDFC24866D9500E1E78C /* PagerContent+Buildable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BC5EDF424866D9500E1E78C /* PagerContent+Buildable.swift */; };
@@ -51,6 +51,7 @@
5151
/* End PBXCopyFilesBuildPhase section */
5252

5353
/* Begin PBXFileReference section */
54+
175117692573D93F00D809CF /* PagerModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PagerModel.swift; path = ../../Sources/SwiftUIPager/PagerModel.swift; sourceTree = "<group>"; };
5455
17D9E0F023D4CF6700C5AE93 /* SwiftUIPagerExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUIPagerExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
5556
17D9E0F323D4CF6700C5AE93 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5657
17D9E0F523D4CF6700C5AE93 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
@@ -67,7 +68,6 @@
6768
6B4EC8A7240D1182001E7490 /* BizarreExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BizarreExampleView.swift; sourceTree = "<group>"; };
6869
6B6FAA3C24D553C8000D1539 /* PagingAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PagingAnimation.swift; path = ../../Sources/SwiftUIPager/PageConfiguration/PagingAnimation.swift; sourceTree = "<group>"; };
6970
6B9C4A8924B45F66004C06C5 /* OnDeactivateModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnDeactivateModifier.swift; sourceTree = "<group>"; };
70-
6BB1AAD224C9C9D20032B5A3 /* PagerProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagerProxy.swift; sourceTree = "<group>"; };
7171
6BB1AAD424C9CA1C0032B5A3 /* PagerContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PagerContent.swift; path = ../../Sources/SwiftUIPager/PagerContent.swift; sourceTree = "<group>"; };
7272
6BB1AAD624C9D0820032B5A3 /* Pager+Buildable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Pager+Buildable.swift"; path = "../../Sources/SwiftUIPager/Pager+Buildable.swift"; sourceTree = "<group>"; };
7373
6BC5EDF424866D9500E1E78C /* PagerContent+Buildable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "PagerContent+Buildable.swift"; path = "../../Sources/SwiftUIPager/PagerContent+Buildable.swift"; sourceTree = "<group>"; };
@@ -147,26 +147,17 @@
147147
path = Examples;
148148
sourceTree = "<group>";
149149
};
150-
6BB1AAD124C9C9370032B5A3 /* Proxy */ = {
151-
isa = PBXGroup;
152-
children = (
153-
6BB1AAD224C9C9D20032B5A3 /* PagerProxy.swift */,
154-
);
155-
name = Proxy;
156-
path = ../../Sources/SwiftUIPager/Proxy;
157-
sourceTree = "<group>";
158-
};
159150
6BC5EDEB24866D6000E1E78C /* Pagination */ = {
160151
isa = PBXGroup;
161152
children = (
162153
6BC5EDF524866D9500E1E78C /* Helpers */,
163154
6BEA730E24ACF8BB007EA8DC /* PageConfiguration */,
164-
6BB1AAD124C9C9370032B5A3 /* Proxy */,
165155
6BC5EDFA24866D9500E1E78C /* Pager.swift */,
166156
6BB1AAD624C9D0820032B5A3 /* Pager+Buildable.swift */,
167157
6BB1AAD424C9CA1C0032B5A3 /* PagerContent.swift */,
168158
6BC5EDF424866D9500E1E78C /* PagerContent+Buildable.swift */,
169159
6BC5EDFB24866D9500E1E78C /* PagerContent+Helper.swift */,
160+
175117692573D93F00D809CF /* PagerModel.swift */,
170161
6BEF676F24C98B62008533FE /* PageWrapper.swift */,
171162
);
172163
name = Pagination;
@@ -274,7 +265,6 @@
274265
buildActionMask = 2147483647;
275266
files = (
276267
6B4EC8A2240D072B001E7490 /* ColorsExampleView.swift in Sources */,
277-
6BB1AAD324C9C9D20032B5A3 /* PagerProxy.swift in Sources */,
278268
17D9E0F423D4CF6700C5AE93 /* AppDelegate.swift in Sources */,
279269
6BEF677024C98B62008533FE /* PageWrapper.swift in Sources */,
280270
6BD3828224C97DE3007B1CF6 /* CGPoint+Angle.swift in Sources */,
@@ -287,6 +277,7 @@
287277
6B9C4A8A24B45F66004C06C5 /* OnDeactivateModifier.swift in Sources */,
288278
6BEA731624ACF8D7007EA8DC /* GesturePriority.swift in Sources */,
289279
6B35B6C125346610000D618F /* PaginationSensitivity.swift in Sources */,
280+
1751176A2573D93F00D809CF /* PagerModel.swift in Sources */,
290281
6BEA731324ACF8D7007EA8DC /* PositionAlignment.swift in Sources */,
291282
6BEA731424ACF8D7007EA8DC /* SwipeDirection.swift in Sources */,
292283
6BC5EE0224866D9500E1E78C /* PagerContent+Helper.swift in Sources */,

Example/SwiftUIPagerExample/Examples/InfiniteExampleView.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ struct InfiniteExampleView: View {
2828
id: \.self) {
2929
self.pageView($0)
3030
}
31+
.singlePagination(ratio: 0.5, sensitivity: .high)
3132
.onPageChanged({ page in
3233
guard page == self.data1.count - 2 else { return }
3334
guard let last = self.data1.last else { return }
@@ -38,7 +39,7 @@ struct InfiniteExampleView: View {
3839
}
3940
})
4041
.pagingPriority(.simultaneous)
41-
.preferredItemSize(CGSize(width: 300, height: 50))
42+
.preferredItemSize(CGSize(width: 200, height: 100))
4243
.itemSpacing(10)
4344
.background(Color.gray.opacity(0.2))
4445
.alert(isPresented: self.$isPresented, content: {

Example/SwiftUIPagerExample/Examples/NestedExampleView.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import SwiftUI
1111
struct NestedExampleView: View {
1212

1313
@State var page: Int = 0
14+
@State var pageOffset: Double = 0
1415
@State var nestedPages: [Int] = [0, 0, 0, 0]
1516

1617
var data = Array(0..<4)
@@ -22,6 +23,7 @@ struct NestedExampleView: View {
2223
id: \.self) { page in
2324
self.nestedPager(page)
2425
}
26+
.pageOffset(pageOffset)
2527
.swipeInteractionArea(.allAvailable)
2628
.background(Color.gray.opacity(0.2))
2729
}
@@ -47,9 +49,26 @@ struct NestedExampleView: View {
4749
id: \.self) { page in
4850
self.pageView(page)
4951
}
52+
.bounces(false)
5053
.onDraggingBegan({
5154
print("Dragging Began")
5255
})
56+
.onDraggingChanged { increment in
57+
withAnimation {
58+
if binding.wrappedValue == self.nestedData.count - 1, increment > 0 {
59+
pageOffset = increment
60+
} else if binding.wrappedValue == 0, increment < 0 {
61+
pageOffset = increment
62+
}
63+
}
64+
}
65+
.onDraggingEnded { increment in
66+
guard pageOffset != 0 else { return }
67+
withAnimation {
68+
pageOffset = 0
69+
page += Int(increment.rounded())
70+
}
71+
}
5372
.itemSpacing(10)
5473
.itemAspectRatio(0.8, alignment: .end)
5574
.padding(8)

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
_SwiftUIPager_ provides a `Pager` component built with SwiftUI native components. `Pager` is a view that renders a scrollable container to display a handful of pages. These pages are recycled on scroll, so you don't have to worry about memory issues. `Pager` will load just a handful of items, enough to beatifully scroll along.
1212

13-
Create vertical or horizontal pagers, align the cards, change the direction of the scroll, animate the pagintation... `Pager` lets you do anything you want.
13+
Create vertical or horizontal pagers, align the cards, change the direction of the scroll, animate the pagination... `Pager` lets you do anything you want.
1414

1515
- [Requirements](#requirements)
1616
- [Installation](#installation)
@@ -26,12 +26,15 @@ Create vertical or horizontal pagers, align the cards, change the direction of t
2626
- [Pagination sensitivity](Documentation/Usage.md#pagination-sensitivity)
2727
- [Orientation and direction](Documentation/Usage.md#orientation-and-direction)
2828
- [Alignment](Documentation/Usage.md#alignment)
29+
- [Partial pagination](Documentation/Usage.md#partial-pagination)
2930
- [Multiple pagination](Documentation/Usage.md#multiple-pagination)
31+
- [More modifiers](Documentation/Usage.md#more-modifiers)
3032
- [Paging Priority](Documentation/Usage.md#paging-priority)
3133
- [Animations](Documentation/Usage.md#animations)
3234
- [Scale](Documentation/Usage.md#scale)
3335
- [Rotation](Documentation/Usage.md#rotation)
3436
- [Loop](Documentation/Usage.md#loop)
37+
- [Page Transitions](Documentation/Usage.md#page-transitions)
3538
- [Add pages on demand](Documentation/Usage.md#add-pages-on-demand)
3639
- [Content Loading Policy](Documentation/Usage.md#content-loading-policy)
3740
- [Examples](Documentation/Usage.md#examples)

Sources/SwiftUIPager/Helpers/Buildable.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
import Foundation
1010

1111
/// Adds a helper function to mutate a properties and help implement _Builder_ pattern
12-
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
12+
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
1313
protocol Buildable { }
1414

15-
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
15+
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
1616
extension Buildable {
1717

1818
/// Mutates a property of the instance

Sources/SwiftUIPager/Helpers/CGPoint+Angle.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import SwiftUI
1010

11-
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
11+
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
1212
extension CGPoint {
1313

1414
var angle: Angle? {
@@ -36,7 +36,7 @@ extension CGPoint {
3636

3737
}
3838

39-
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
39+
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
4040
extension Angle {
4141
var isAlongXAxis: Bool {
4242
let degrees = ((Int(self.degrees.rounded()) % 360) + 360) % 360

Sources/SwiftUIPager/Helpers/OnDeactivateModifier.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import SwiftUI
1010

1111
/// This modifier allows the `View` to listen to the `UIScene.didActivateNotification` in `iOS`
1212
/// and perform an action when received.
13-
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
13+
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
1414
struct OnDeactivateView<Content: View>: View {
1515

1616
var content: Content
@@ -29,7 +29,7 @@ struct OnDeactivateView<Content: View>: View {
2929

3030
}
3131

32-
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
32+
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
3333
extension View {
3434

3535
func onDeactivate(perform: @escaping () -> Void) -> some View {

Sources/SwiftUIPager/Helpers/View+Helper.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import SwiftUI
1010

11-
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
11+
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
1212
extension View {
1313

1414
func frame(size: CGSize) -> some View {

0 commit comments

Comments
 (0)