Skip to content

Commit

Permalink
Merge pull request #162 from fermoya/feat/bounce-workaround
Browse files Browse the repository at this point in the history
workaround binding update issue
  • Loading branch information
fermoya authored Nov 29, 2020
2 parents 3e091c5 + 7b1b4a8 commit ea83d9d
Show file tree
Hide file tree
Showing 22 changed files with 85 additions and 40 deletions.
4 changes: 4 additions & 0 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 Down Expand Up @@ -50,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 Down Expand Up @@ -155,6 +157,7 @@
6BB1AAD424C9CA1C0032B5A3 /* PagerContent.swift */,
6BC5EDF424866D9500E1E78C /* PagerContent+Buildable.swift */,
6BC5EDFB24866D9500E1E78C /* PagerContent+Helper.swift */,
175117692573D93F00D809CF /* PagerModel.swift */,
6BEF676F24C98B62008533FE /* PageWrapper.swift */,
);
name = Pagination;
Expand Down Expand Up @@ -274,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
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
4 changes: 2 additions & 2 deletions Sources/SwiftUIPager/PageConfiguration/SwipeDirection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation

/// Swipe direction for a vertical `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 HorizontalSwipeDirection {

/// Pages move from left to right
Expand All @@ -20,7 +20,7 @@ public enum HorizontalSwipeDirection {
}

/// Swipe direction for a horizontal `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 VerticalSwipeDirection {

/// Pages move from top left to bottom
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import SwiftUI

/// Defines the area in `Pager` that allows hits and listens to swipes
@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 SwipeInteractionArea {

/// All available space inside `Pager`
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftUIPager/PageWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation

/// Wrapper to `Pager` elements. It allows `Pager` to replicate the input data elements if required by the user.
@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 PageWrapper<Element, ID>: Equatable, Identifiable where Element: Equatable, ID: Hashable {

/// This _id_ indicates batch iteration
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftUIPager/Pager+Buildable.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 Pager: Buildable {

/// Result of paginating
Expand Down
13 changes: 10 additions & 3 deletions Sources/SwiftUIPager/Pager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import SwiftUI
/// - 30 px of vertical insets
/// - 0.6 shrink ratio for items that aren't focused.
///
@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 struct Pager<Element, ID, PageView>: View where PageView: View, Element: Equatable, ID: Hashable {

/// `Direction` determines the direction of the swipe gesture
Expand Down Expand Up @@ -174,6 +174,8 @@ public struct Pager<Element, ID, PageView>: View where PageView: View, Element:
}
}

@ObservedObject var pagerModel: PagerModel

/// Initializes a new `Pager`.
///
/// - Parameter page: Binding to the page index
Expand All @@ -182,6 +184,7 @@ public struct Pager<Element, ID, PageView>: View where PageView: View, Element:
/// - Parameter content: Factory method to build new pages
public init<Data: RandomAccessCollection>(page: Binding<Int>, data: Data, id: KeyPath<Element, ID>, @ViewBuilder content: @escaping (Element) -> PageView) where Data.Index == Int, Data.Element == Element {
self._page = page
self.pagerModel = PagerModel(page: page.wrappedValue)
self.data = Array(data)
self.id = id
self.content = content
Expand All @@ -190,14 +193,18 @@ public struct Pager<Element, ID, PageView>: View where PageView: View, Element:
public var body: some View {
GeometryReader { proxy in
self.content(for: proxy.size)
.environmentObject(pagerModel)
.onReceive(pagerModel.$page) { (page) in
self.page = page
}
}
.clipped()
}

func content(for size: CGSize) -> PagerContent {
var pagerContent =
PagerContent(size: size,
page: $page,
pagerModel: pagerModel,
data: data,
id: id,
content: content)
Expand Down Expand Up @@ -239,7 +246,7 @@ public struct Pager<Element, ID, PageView>: View where PageView: View, Element:

}

@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 Pager where ID == Element.ID, Element : Identifiable {

/// Initializes a new Pager.
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftUIPager/PagerContent+Buildable.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 Pager.PagerContent: Buildable {

/// Sets the animation to be applied when the user stops dragging
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftUIPager/PagerContent+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 Pager.PagerContent {

/// Manages the number of items that should be displayed in the screen.
Expand All @@ -23,7 +23,7 @@ extension Pager.PagerContent {

/// Work around to avoid @State keeps wrong value
var page: Int {
return min(pageIndex, numberOfPages - 1)
return min(pagerModel.page, numberOfPages - 1)
}

/// `true` if `Pager` is vertical
Expand Down
21 changes: 9 additions & 12 deletions Sources/SwiftUIPager/PagerContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SwiftUI
///
/// `PagerContent` is the content of `Pager`. This view is needed so that `Pager` wrapps it around a `GeometryReader ` and passes the size in its initializer.
///
@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 Pager {
struct PagerContent: View {

Expand Down Expand Up @@ -147,21 +147,18 @@ extension Pager {
@State var pageIncrement = 1

/// Page index
@Binding var pageIndex: Int {
didSet {
onPageChanged?(page)
}
}
@ObservedObject var pagerModel: PagerModel

/// Initializes a new `Pager`.
///
/// - Parameter page: Binding to the page index
/// - Parameter size: Available size
/// - Parameter pagerModel: Wrapper for the current page
/// - Parameter data: Array of items to populate the content
/// - Parameter id: KeyPath to identifiable property
/// - Parameter content: Factory method to build new pages
init(size: CGSize, page: Binding<Int>, data: [Element], id: KeyPath<Element, ID>, @ViewBuilder content: @escaping (Element) -> PageView) {
init(size: CGSize, pagerModel: PagerModel, data: [Element], id: KeyPath<Element, ID>, @ViewBuilder content: @escaping (Element) -> PageView) {
self.size = size
self._pageIndex = page
self.pagerModel = pagerModel
self.data = data.map { PageWrapper(batchId: 1, keyPath: id, element: $0) }
self.id = \PageWrapper<Element, ID>.id
self.content = content
Expand Down Expand Up @@ -214,7 +211,7 @@ extension Pager {

// MARK: 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, *)
extension Pager.PagerContent {

/// `DragGesture` customized to work with `Pager`
Expand Down Expand Up @@ -283,13 +280,13 @@ extension Pager.PagerContent {
speed = 1 / min(4, Double(pageIncrement))
}

let pagingAnimation = self.pagingAnimation?((pageIndex, newPage, draggingOffset, draggingVelocity)) ?? defaultPagingAnimation
let pagingAnimation = self.pagingAnimation?((page, newPage, draggingOffset, draggingVelocity)) ?? defaultPagingAnimation

let animation = pagingAnimation.animation.speed(speed)
withAnimation(animation) {
self.draggingOffset = 0
self.pageIncrement = pageIncrement
self.pageIndex = newPage
self.pagerModel.page = newPage
self.draggingVelocity = 0
self.lastDraggingValue = nil
}
Expand Down
22 changes: 22 additions & 0 deletions Sources/SwiftUIPager/PagerModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// Pager+Helper.swift
// SwiftUIPager
//
// Created by Fernando Moya de Rivas on 19/01/2020.
// Copyright © 2020 Fernando Moya de Rivas. All rights reserved.
//

import SwiftUI

/// Workaround to avoid `Binding` updating after rest of `State` after drag ends
/// More info [here](https://developer.apple.com/forums/thread/667988) and [here](https://developer.apple.com/forums/thread/667720)
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
class PagerModel: ObservableObject {

@Published var page: Int

init(page: Int) {
self.page = page
}

}
Loading

0 comments on commit ea83d9d

Please sign in to comment.