From 80a31d0e6d95f7216eb7bfe16f66d8b533fe857b Mon Sep 17 00:00:00 2001 From: fermoya Date: Mon, 12 Apr 2021 21:03:30 +0100 Subject: [PATCH] Allow to choose dragging and paging animations #minor --- Documentation/Usage.md | 2 +- .../PageConfiguration/PagingAnimation.swift | 9 ++++++--- Sources/SwiftUIPager/Pager+Buildable.swift | 20 ++++++++++++++++--- Sources/SwiftUIPager/Pager.swift | 2 +- .../SwiftUIPager/PagerContent+Buildable.swift | 20 ++++++++++++++++--- Sources/SwiftUIPager/PagerContent.swift | 8 ++++---- .../Pager+Buildable_Tests.swift | 13 +++++++++++- .../PagingAnimation_Tests.swift | 7 ++++++- release_description.md | 14 +++---------- 9 files changed, 67 insertions(+), 28 deletions(-) diff --git a/Documentation/Usage.md b/Documentation/Usage.md index b3a5060..8e505b0 100644 --- a/Documentation/Usage.md +++ b/Documentation/Usage.md @@ -227,7 +227,7 @@ Transform your `Pager` into an endless sroll by using `loopPages`: ## Page Transitions -Use `draggingAnimation(_:)` to customize the _transition_ to the next page. +Use `draggingAnimation(onChange:, onEnded:)` to customize the animations applied while dragging items and/or transitioning to the next element. ```swift Pager(...) diff --git a/Sources/SwiftUIPager/PageConfiguration/PagingAnimation.swift b/Sources/SwiftUIPager/PageConfiguration/PagingAnimation.swift index b0eb9c6..e3bc6cb 100644 --- a/Sources/SwiftUIPager/PageConfiguration/PagingAnimation.swift +++ b/Sources/SwiftUIPager/PageConfiguration/PagingAnimation.swift @@ -33,7 +33,7 @@ public enum PagingAnimation: Equatable { /// Pass your custom animation /// /// - Parameter animation: animation to be applied - case custom(animation: Animation) + case custom(animation: Animation?) /// Standard animation. Single pagination `Pager` defaults to this value. Ease-out, 0.35 seconds. public static var standard: PagingAnimation = .standard(duration: 0.35) @@ -42,15 +42,18 @@ public enum PagingAnimation: Equatable { /// See the bezier curve [here](https://cubic-bezier.com/#.2,1,.9,1). public static var steep: PagingAnimation = .steep(duration: 0.2) + /// No animation applied + public static var none: PagingAnimation = .custom(animation: nil) + /// Translates the option selected to a `SwiftUI` animation - var animation: Animation { + var animation: Animation? { switch self { case .steep(let duration): return Animation.timingCurve(0.2, 1, 0.9, 1, duration: duration) case .standard(let duration): return Animation.easeOut(duration: duration) case .interactive: - return .interactiveSpring(response: 0.3, dampingFraction: 0.8, blendDuration: 0.5) + return .interactiveSpring() case .custom(let animation): return animation } diff --git a/Sources/SwiftUIPager/Pager+Buildable.swift b/Sources/SwiftUIPager/Pager+Buildable.swift index 75fdc49..098a84c 100644 --- a/Sources/SwiftUIPager/Pager+Buildable.swift +++ b/Sources/SwiftUIPager/Pager+Buildable.swift @@ -70,13 +70,27 @@ extension Pager: Buildable { #if !os(tvOS) - /// Sets the explicit animation used for dragging + /// Sets the explicit animation to be used. Defaults to `.standard` /// - /// - Parameter value: explicit animation - public func draggingAnimation(_ value: DraggingAnimation?) -> Self { + /// - Parameter value: animation to use while dragging and to page + /// + /// - Warning: `spring` animations don't work well. Avoid high responses while dragging as the animation should be short + public func draggingAnimation(_ value: DraggingAnimation) -> Self { mutating(keyPath: \.draggingAnimation, value: value) } + /// Sets the explicit animation to be used. Defaults to `.standard` + /// + /// - Parameter anim1: animation to use while dragging + /// - Parameter anim2: animation to use to page + /// + /// - Note: Setting different animations could cause unexpected behavior + /// - Warning: `spring` animations don't work well. Avoid high responses while dragging as the animation should be short + public func draggingAnimation(onChange anim1: DraggingAnimation, onEnded anim2: DraggingAnimation) -> Self { + mutating(keyPath: \.draggingAnimation, value: anim1) + .mutating(keyPath: \.pagingAnimation, value: { _ in anim2 }) + } + /// Sensitivity used to determine whether or not to swipe the page /// /// - Parameter value: sensitivity to be applied when paginating diff --git a/Sources/SwiftUIPager/Pager.swift b/Sources/SwiftUIPager/Pager.swift index 14c58ee..55b330d 100644 --- a/Sources/SwiftUIPager/Pager.swift +++ b/Sources/SwiftUIPager/Pager.swift @@ -73,7 +73,7 @@ public struct Pager: View where PageView: View, Element: var pagingAnimation: ((DragResult) -> PagingAnimation)? /// Animation used for dragging - var draggingAnimation: DraggingAnimation? + var draggingAnimation: DraggingAnimation = .standard /// Sensitivity used to determine whether or not to swipe the page var sensitivity: PaginationSensitivity = .default diff --git a/Sources/SwiftUIPager/PagerContent+Buildable.swift b/Sources/SwiftUIPager/PagerContent+Buildable.swift index 56b9c4f..98c2e10 100644 --- a/Sources/SwiftUIPager/PagerContent+Buildable.swift +++ b/Sources/SwiftUIPager/PagerContent+Buildable.swift @@ -70,13 +70,27 @@ extension Pager.PagerContent: Buildable { #if !os(tvOS) - /// Sets the explicit animation used for dragging + /// Sets the explicit animation to be used. Defaults to `.standard` /// - /// - Parameter value: explicit animation - func draggingAnimation(_ value: DraggingAnimation?) -> Self { + /// - Parameter value: animation to use while dragging and to page + /// + /// - Warning: `spring` animations don't work well. Avoid high responses while dragging as the animation should be short + func draggingAnimation(_ value: DraggingAnimation) -> Self { mutating(keyPath: \.draggingAnimation, value: value) } + /// Sets the explicit animation to be used. Defaults to `.standard` + /// + /// - Parameter anim1: animation to use while dragging + /// - Parameter anim2: animation to use to page + /// + /// - Note: Setting different animations could cause unexpected behavior + /// - Warning: `spring` animations don't work well. Avoid high responses while dragging as the animation should be short + func draggingAnimation(onChange anim1: DraggingAnimation, onEnded anim2: DraggingAnimation) -> Self { + mutating(keyPath: \.draggingAnimation, value: anim1) + .mutating(keyPath: \.pagingAnimation, value: { _ in anim2 }) + } + /// Sensitivity used to determine whether or not to swipe the page /// /// - Parameter value: sensitivity to be applied when paginating diff --git a/Sources/SwiftUIPager/PagerContent.swift b/Sources/SwiftUIPager/PagerContent.swift index dfff32f..a28fe19 100644 --- a/Sources/SwiftUIPager/PagerContent.swift +++ b/Sources/SwiftUIPager/PagerContent.swift @@ -57,7 +57,7 @@ extension Pager { var pagingAnimation: ((DragResult) -> PagingAnimation)? /// Animation used for dragging - var draggingAnimation: DraggingAnimation? + var draggingAnimation: DraggingAnimation = .standard /// Sensitivity used to determine whether or not to swipe the page var sensitivity: PaginationSensitivity = .default @@ -225,7 +225,7 @@ extension Pager.PagerContent { } func onDragChanged(with value: DragGesture.Value) { - let animation = draggingAnimation?.animation ?? .default + let animation = draggingAnimation.animation withAnimation(animation) { if self.lastDraggingValue == nil { onDraggingBegan?() @@ -280,9 +280,9 @@ extension Pager.PagerContent { speed = 1 / min(4, Double(pageIncrement)) } - let pagingAnimation = self.draggingAnimation ?? self.pagingAnimation?((page, newPage, draggingOffset, draggingVelocity)) ?? defaultPagingAnimation + let pagingAnimation = self.pagingAnimation?((page, newPage, draggingOffset, draggingVelocity)) ?? defaultPagingAnimation - let animation = pagingAnimation.animation.speed(speed) + let animation = pagingAnimation.animation?.speed(speed) if page != newPage { onPageWillChange?(newPage) } diff --git a/Tests/SwiftUIPagerTests/Pager+Buildable_Tests.swift b/Tests/SwiftUIPagerTests/Pager+Buildable_Tests.swift index 948d9fa..a594532 100644 --- a/Tests/SwiftUIPagerTests/Pager+Buildable_Tests.swift +++ b/Tests/SwiftUIPagerTests/Pager+Buildable_Tests.swift @@ -35,7 +35,7 @@ final class Pager_Buildable_Tests: XCTestCase { XCTAssertEqual(pager.contentLoadingPolicy, .default) XCTAssertEqual(pager.allowsMultiplePagination, false) XCTAssertNil(pager.pagingAnimation) - XCTAssertNil(pager.draggingAnimation) + XCTAssertEqual(pager.draggingAnimation, PagingAnimation.standard) XCTAssertEqual(pager.sensitivity, .default) XCTAssertEqual(pager.pageRatio, 1) XCTAssertTrue(pager.bounces) @@ -122,6 +122,16 @@ final class Pager_Buildable_Tests: XCTestCase { XCTAssertEqual(animation, PagingAnimation.steep) } + func test_GivenPager_WhenDraggingAnimation_ThenDraggingAndPagingAnimationNotNil() throws { + var pager = givenPager + pager = pager.draggingAnimation(onChange: .interactive, onEnded: .steep) + let pagerContent = pager.content(for: CGSize(width: 100, height: 100)) + let anim1 = try XCTUnwrap(pagerContent.draggingAnimation) + let anim2 = try XCTUnwrap(pagerContent.pagingAnimation?((0, 0, 0, 0))) + XCTAssertEqual(anim1, PagingAnimation.interactive) + XCTAssertEqual(anim2, PagingAnimation.steep) + } + func test_GivenPager_WhenMultiplePagination_ThenAllowsMultiplePagination() { var pager = givenPager pager = pager.multiplePagination() @@ -617,6 +627,7 @@ final class Pager_Buildable_Tests: XCTestCase { ("test_GivenPagerWith3DRotation_WhenInteractive_ThenInteractiveScaleNotChanged", test_GivenPagerWith3DRotation_WhenInteractive_ThenInteractiveScaleNotChanged), ("test_GivenPager_When3DRotation_ThenShouldRotate", test_GivenPager_When3DRotation_ThenShouldRotate), ("test_GivenPagerWith3DRotation_When3DRotationFalse_ThenShouldRotateFalse", test_GivenPagerWith3DRotation_When3DRotationFalse_ThenShouldRotateFalse), + ("test_GivenPager_WhenDraggingAnimation_ThenDraggingAndPagingAnimationNotNil", test_GivenPager_WhenDraggingAnimation_ThenDraggingAndPagingAnimationNotNil), ("test_GivenPager_WhenDraggingAnimation_ThenDraggingAnimationNotNil", test_GivenPager_WhenDraggingAnimation_ThenDraggingAnimationNotNil), ("test_GivenPager_WhenHorizontalRightToLeft_ThenScrollAngle", test_GivenPager_WhenHorizontalRightToLeft_ThenScrollAngle), ("test_GivenPager_WhenAlignment_ThenAlignmentSet", test_GivenPager_WhenAlignment_ThenAlignmentSet), diff --git a/Tests/SwiftUIPagerTests/PagingAnimation_Tests.swift b/Tests/SwiftUIPagerTests/PagingAnimation_Tests.swift index 76eb3f9..6cd8f95 100644 --- a/Tests/SwiftUIPagerTests/PagingAnimation_Tests.swift +++ b/Tests/SwiftUIPagerTests/PagingAnimation_Tests.swift @@ -31,12 +31,17 @@ final class PagingAnimation_Tests: XCTestCase { } func test_GivenPagingAnimationInteractive_WhenAnimation_ThenExpectedAnimationValues() { - let input = Animation.interactiveSpring(response: 0.3, dampingFraction: 0.8, blendDuration: 0.5) + let input = Animation.interactiveSpring() let pagingAnimation: PagingAnimation = .interactive let animation = pagingAnimation.animation XCTAssertEqual(animation, input) } + func test_GivenPagingAnimationNone_WhenAnimation_ThenNil() { + let pagingAnimation: PagingAnimation = .none + XCTAssertNil(pagingAnimation.animation) + } + static var allTests = [ ("test_GivenPagingAnimationSteep_WhenAnimation_ThenExpectedAnimationValues", test_GivenPagingAnimationSteep_WhenAnimation_ThenExpectedAnimationValues), ("test_GivenPagingAnimationStandard_WhenAnimation_ThenExpectedAnimationValues", test_GivenPagingAnimationStandard_WhenAnimation_ThenExpectedAnimationValues), diff --git a/release_description.md b/release_description.md index 6d830fb..a9dcb6e 100644 --- a/release_description.md +++ b/release_description.md @@ -1,14 +1,6 @@ ### Features -- New `interactive(opacity:)` to add an interactive fade in/out effect to the scroll -- New `interactive(scale:)` and `interactive(rotation:)` -- Interactive effects can be now combined -- CI/CD to build `legacy-projects` branch against _iOS 12_ - -### Fixes -- Items not scrolling in _iOS 13_ -- #193 Transitions are jumpy if fast -- #194 `AnimatableModifier` symbol not found +- New `dragggingAnimation(_:)` modifier to select an animation to use while dragging +- Convenience builder methods `none` and `interactiveSpring` to create `DraggingAnimation` ### Deprecations -- `rotation3D()` in favor of `interactive(rotation:)` -- `interactive(_:)` in favor of `interactive(scale:)` \ No newline at end of file +- `pagingAnimation(_:)` in favor of `dragggingAnimation(_:)` \ No newline at end of file