Skip to content

Commit

Permalink
Merge pull request #163 from fermoya/develop
Browse files Browse the repository at this point in the history
Release 1.14.0
  • Loading branch information
fermoya authored Nov 29, 2020
2 parents 2c7816c + ea83d9d commit dd9d133
Show file tree
Hide file tree
Showing 30 changed files with 377 additions and 293 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ jobs:
steps:
- uses: actions/checkout@v2

- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable

- name: Create XCFramework
id: xcframework
run: ./scripts/build_xcframework.sh
Expand Down
69 changes: 69 additions & 0 deletions Documentation/Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@ Pager(...)

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

### Partial pagination

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:

```swift
Pager(...)
.singlePagination(0.33, sensitivity: .custom(0.2))
.preferredItemSize(CGSize(width: 300, height: 400))
.itemSpacing(10)
.background(Color.gray.opacity(0.2))
```
<img src="/resources/usage/single-pagination-ratio.gif" alt="Reveal Ratio set to a third of the page" height="640"/>

For more information about `sensitivity`, check out [Pagination sensitivity](#pagination-sensitivity).

### Multiple pagination

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`.
Expand All @@ -112,6 +127,17 @@ Pager(...)

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

### More modifiers
| **Modifier** | **Description** |
|---|---|
| `allowsDragging` | whether or not dragging is allowed |
| `disableDragging` | disables dragging |
| `bounces` | whether or not `Pager` should bounce |
| `delaysTouches` | whether or not touches shoulf be delayed. Useful if nested in `ScrollView` |
| `pageOffset` | allows _manual_ scroll |
| `expandPageToEdges` | modifies `itemAspectRatio` so that the use up all the space available |


## Paging Priority

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:
Expand Down Expand Up @@ -182,6 +208,23 @@ Transform your `Pager` into an endless sroll by using `loopPages`:

**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.

## Page Transitions

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:
* Current page
* Next page
* Total shift
* Velocity

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:

```swift
Pager(...)
.pagingAnimation({ currentPage, nextPage, totalShift, velocity in
return PagingAnimation.custom(animation: .easeInOut)
})
```

## Events

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

You can also use `onDraggingBegan`, `onDraggingChanged` and `onDragginEnded` to keep track of the dragging.

## Add pages on demand

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

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:

```swift

@State var count: Int = -1
@State var page: Int = 3
@State var data = Array(0..<5)

Pager(page: self.$page,
data: self.data,
id: \.self) {
self.pageView($0)
}
.onPageChanged({ page in
guard page == 1 else { return }
let newData = (1...5).map { $0 * self.count }
withAnimation {
self.data1.insert(contentsOf: newData, at: 0)
self.page1 += 5
self.count -= 1
}
})
```

## Content Loading Policy

`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.
Expand Down
17 changes: 4 additions & 13 deletions Example/SwiftUIPagerExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
1751176A2573D93F00D809CF /* PagerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 175117692573D93F00D809CF /* PagerModel.swift */; };
17D9E0F423D4CF6700C5AE93 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D9E0F323D4CF6700C5AE93 /* AppDelegate.swift */; };
17D9E0F623D4CF6700C5AE93 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D9E0F523D4CF6700C5AE93 /* SceneDelegate.swift */; };
17D9E0F823D4CF6700C5AE93 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D9E0F723D4CF6700C5AE93 /* ContentView.swift */; };
Expand All @@ -20,7 +21,6 @@
6B4EC8A8240D1182001E7490 /* BizarreExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B4EC8A7240D1182001E7490 /* BizarreExampleView.swift */; };
6B6FAA3D24D553C8000D1539 /* PagingAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B6FAA3C24D553C8000D1539 /* PagingAnimation.swift */; };
6B9C4A8A24B45F66004C06C5 /* OnDeactivateModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B9C4A8924B45F66004C06C5 /* OnDeactivateModifier.swift */; };
6BB1AAD324C9C9D20032B5A3 /* PagerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB1AAD224C9C9D20032B5A3 /* PagerProxy.swift */; };
6BB1AAD524C9CA1C0032B5A3 /* PagerContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB1AAD424C9CA1C0032B5A3 /* PagerContent.swift */; };
6BB1AAD724C9D0820032B5A3 /* Pager+Buildable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BB1AAD624C9D0820032B5A3 /* Pager+Buildable.swift */; };
6BC5EDFC24866D9500E1E78C /* PagerContent+Buildable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BC5EDF424866D9500E1E78C /* PagerContent+Buildable.swift */; };
Expand Down Expand Up @@ -51,6 +51,7 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
175117692573D93F00D809CF /* PagerModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PagerModel.swift; path = ../../Sources/SwiftUIPager/PagerModel.swift; sourceTree = "<group>"; };
17D9E0F023D4CF6700C5AE93 /* SwiftUIPagerExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftUIPagerExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
17D9E0F323D4CF6700C5AE93 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
17D9E0F523D4CF6700C5AE93 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand All @@ -67,7 +68,6 @@
6B4EC8A7240D1182001E7490 /* BizarreExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BizarreExampleView.swift; sourceTree = "<group>"; };
6B6FAA3C24D553C8000D1539 /* PagingAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PagingAnimation.swift; path = ../../Sources/SwiftUIPager/PageConfiguration/PagingAnimation.swift; sourceTree = "<group>"; };
6B9C4A8924B45F66004C06C5 /* OnDeactivateModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnDeactivateModifier.swift; sourceTree = "<group>"; };
6BB1AAD224C9C9D20032B5A3 /* PagerProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagerProxy.swift; sourceTree = "<group>"; };
6BB1AAD424C9CA1C0032B5A3 /* PagerContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PagerContent.swift; path = ../../Sources/SwiftUIPager/PagerContent.swift; sourceTree = "<group>"; };
6BB1AAD624C9D0820032B5A3 /* Pager+Buildable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Pager+Buildable.swift"; path = "../../Sources/SwiftUIPager/Pager+Buildable.swift"; sourceTree = "<group>"; };
6BC5EDF424866D9500E1E78C /* PagerContent+Buildable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "PagerContent+Buildable.swift"; path = "../../Sources/SwiftUIPager/PagerContent+Buildable.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -147,26 +147,17 @@
path = Examples;
sourceTree = "<group>";
};
6BB1AAD124C9C9370032B5A3 /* Proxy */ = {
isa = PBXGroup;
children = (
6BB1AAD224C9C9D20032B5A3 /* PagerProxy.swift */,
);
name = Proxy;
path = ../../Sources/SwiftUIPager/Proxy;
sourceTree = "<group>";
};
6BC5EDEB24866D6000E1E78C /* Pagination */ = {
isa = PBXGroup;
children = (
6BC5EDF524866D9500E1E78C /* Helpers */,
6BEA730E24ACF8BB007EA8DC /* PageConfiguration */,
6BB1AAD124C9C9370032B5A3 /* Proxy */,
6BC5EDFA24866D9500E1E78C /* Pager.swift */,
6BB1AAD624C9D0820032B5A3 /* Pager+Buildable.swift */,
6BB1AAD424C9CA1C0032B5A3 /* PagerContent.swift */,
6BC5EDF424866D9500E1E78C /* PagerContent+Buildable.swift */,
6BC5EDFB24866D9500E1E78C /* PagerContent+Helper.swift */,
175117692573D93F00D809CF /* PagerModel.swift */,
6BEF676F24C98B62008533FE /* PageWrapper.swift */,
);
name = Pagination;
Expand Down Expand Up @@ -274,7 +265,6 @@
buildActionMask = 2147483647;
files = (
6B4EC8A2240D072B001E7490 /* ColorsExampleView.swift in Sources */,
6BB1AAD324C9C9D20032B5A3 /* PagerProxy.swift in Sources */,
17D9E0F423D4CF6700C5AE93 /* AppDelegate.swift in Sources */,
6BEF677024C98B62008533FE /* PageWrapper.swift in Sources */,
6BD3828224C97DE3007B1CF6 /* CGPoint+Angle.swift in Sources */,
Expand All @@ -287,6 +277,7 @@
6B9C4A8A24B45F66004C06C5 /* OnDeactivateModifier.swift in Sources */,
6BEA731624ACF8D7007EA8DC /* GesturePriority.swift in Sources */,
6B35B6C125346610000D618F /* PaginationSensitivity.swift in Sources */,
1751176A2573D93F00D809CF /* PagerModel.swift in Sources */,
6BEA731324ACF8D7007EA8DC /* PositionAlignment.swift in Sources */,
6BEA731424ACF8D7007EA8DC /* SwipeDirection.swift in Sources */,
6BC5EE0224866D9500E1E78C /* PagerContent+Helper.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ struct InfiniteExampleView: View {
id: \.self) {
self.pageView($0)
}
.singlePagination(ratio: 0.5, sensitivity: .high)
.onPageChanged({ page in
guard page == self.data1.count - 2 else { return }
guard let last = self.data1.last else { return }
Expand All @@ -38,7 +39,7 @@ struct InfiniteExampleView: View {
}
})
.pagingPriority(.simultaneous)
.preferredItemSize(CGSize(width: 300, height: 50))
.preferredItemSize(CGSize(width: 200, height: 100))
.itemSpacing(10)
.background(Color.gray.opacity(0.2))
.alert(isPresented: self.$isPresented, content: {
Expand Down
19 changes: 19 additions & 0 deletions Example/SwiftUIPagerExample/Examples/NestedExampleView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import SwiftUI
struct NestedExampleView: View {

@State var page: Int = 0
@State var pageOffset: Double = 0
@State var nestedPages: [Int] = [0, 0, 0, 0]

var data = Array(0..<4)
Expand All @@ -22,6 +23,7 @@ struct NestedExampleView: View {
id: \.self) { page in
self.nestedPager(page)
}
.pageOffset(pageOffset)
.swipeInteractionArea(.allAvailable)
.background(Color.gray.opacity(0.2))
}
Expand All @@ -47,9 +49,26 @@ struct NestedExampleView: View {
id: \.self) { page in
self.pageView(page)
}
.bounces(false)
.onDraggingBegan({
print("Dragging Began")
})
.onDraggingChanged { increment in
withAnimation {
if binding.wrappedValue == self.nestedData.count - 1, increment > 0 {
pageOffset = increment
} else if binding.wrappedValue == 0, increment < 0 {
pageOffset = increment
}
}
}
.onDraggingEnded { increment in
guard pageOffset != 0 else { return }
withAnimation {
pageOffset = 0
page += Int(increment.rounded())
}
}
.itemSpacing(10)
.itemAspectRatio(0.8, alignment: .end)
.padding(8)
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

_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.

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

- [Requirements](#requirements)
- [Installation](#installation)
Expand All @@ -26,12 +26,15 @@ Create vertical or horizontal pagers, align the cards, change the direction of t
- [Pagination sensitivity](Documentation/Usage.md#pagination-sensitivity)
- [Orientation and direction](Documentation/Usage.md#orientation-and-direction)
- [Alignment](Documentation/Usage.md#alignment)
- [Partial pagination](Documentation/Usage.md#partial-pagination)
- [Multiple pagination](Documentation/Usage.md#multiple-pagination)
- [More modifiers](Documentation/Usage.md#more-modifiers)
- [Paging Priority](Documentation/Usage.md#paging-priority)
- [Animations](Documentation/Usage.md#animations)
- [Scale](Documentation/Usage.md#scale)
- [Rotation](Documentation/Usage.md#rotation)
- [Loop](Documentation/Usage.md#loop)
- [Page Transitions](Documentation/Usage.md#page-transitions)
- [Add pages on demand](Documentation/Usage.md#add-pages-on-demand)
- [Content Loading Policy](Documentation/Usage.md#content-loading-policy)
- [Examples](Documentation/Usage.md#examples)
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftUIPager/Helpers/Buildable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
import Foundation

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

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Buildable {

/// Mutates a property of the instance
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftUIPager/Helpers/CGPoint+Angle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import SwiftUI

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension CGPoint {

var angle: Angle? {
Expand Down Expand Up @@ -36,7 +36,7 @@ extension CGPoint {

}

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension Angle {
var isAlongXAxis: Bool {
let degrees = ((Int(self.degrees.rounded()) % 360) + 360) % 360
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftUIPager/Helpers/OnDeactivateModifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import SwiftUI

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

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

}

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {

func onDeactivate(perform: @escaping () -> Void) -> some View {
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftUIPager/Helpers/View+Helper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import SwiftUI

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {

func frame(size: CGSize) -> some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

/// Policy to follow when loading content
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public enum ContentLoadingPolicy: Equatable {

/// Content is loaded on demand by applying a recycling the ratio.
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftUIPager/PageConfiguration/GesturePriority.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import SwiftUI

/// Defines a set of priorities to interact with gestures
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public enum GesturePriority {
/// Refers to `highPriorityGesture` modifier
case high
Expand All @@ -24,7 +24,7 @@ public enum GesturePriority {
static let `default`: GesturePriority = .normal
}

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
extension View {

func gesture<T>(_ gesture: T, priority: GesturePriority) -> some View where T : Gesture {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import CoreGraphics

/// Defines how sensitive the pagination is to determine whether or not to move to the next the page.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public enum PaginationSensitivity: Equatable {

/// The shift relative to container size needs to be greater than or equal to 75%
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import SwiftUI

/// Animation to be used when the user stops dragging
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public enum PagingAnimation: Equatable {

/// Highly steep curve. Very fast on start, slow on end.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import CoreGraphics

/// `Alignment` determines the focused page alignment inside `Pager`
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public enum PositionAlignment {
/// Sets the alignment to be centered
case center
Expand Down
Loading

0 comments on commit dd9d133

Please sign in to comment.