From 91563d2b858759680a7ba9e2abd5568daf7395b7 Mon Sep 17 00:00:00 2001 From: fermoya Date: Fri, 5 Mar 2021 13:41:58 +0000 Subject: [PATCH] Rename animations to interactive --- Documentation/Usage.md | 15 ++--- .../Examples/BizarreExampleView.swift | 2 +- .../Examples/EmbeddedExampleView.swift | 4 +- README.md | 1 + Sources/SwiftUIPager/Pager+Buildable.swift | 30 +++++++--- Sources/SwiftUIPager/Pager.swift | 21 ++++--- .../SwiftUIPager/PagerContent+Buildable.swift | 22 +++---- Sources/SwiftUIPager/PagerContent.swift | 3 - .../Pager+Buildable_Tests.swift | 19 +++++- .../PagerContent+Helper_Tests.swift | 60 ++++++++++++------- 10 files changed, 113 insertions(+), 64 deletions(-) diff --git a/Documentation/Usage.md b/Documentation/Usage.md index b220be2..ce60918 100644 --- a/Documentation/Usage.md +++ b/Documentation/Usage.md @@ -182,22 +182,22 @@ func pageView(_ page: Int) -> some View { ### Scale -Use `interactive` to add a scale animation effect to those pages that are unfocused, that is, those elements whose index is different from `pageIndex`: +Use `interactive(scale:)` to add a scale animation effect to those pages that are unfocused, that is, those elements whose index is different from `pageIndex`: ```swift Pager(...) - .interactive(0.8) + .interactive(scale: 0.8) ``` Interactive pager -### Faded +### Opacity -Get a interactive fading effect on your items by using `faded`: +Get a interactive fading effect on your items by using `interactive(opacity:)`: ```swift Pager(...) - .faded(0.4) + .interacive(opacity: 0.4) .preferredItemSize(CGSize(width: 150, height: 150)) .itemSpacing(10) ``` @@ -206,12 +206,13 @@ Pager(...) ### Rotation -You can also use `rotation3D` to add a rotation effect to your pages: +You can also use `interactive(rotation:)` to add a rotation effect to your pages: ```swift Pager(...) .itemSpacing(10) - .rotation3D() + .interactive(rotation: true) + .interactive(scale: 0.7) ``` Rotation 3D diff --git a/Example/SwiftUIPagerExample/Examples/BizarreExampleView.swift b/Example/SwiftUIPagerExample/Examples/BizarreExampleView.swift index 13d4259..a30238e 100644 --- a/Example/SwiftUIPagerExample/Examples/BizarreExampleView.swift +++ b/Example/SwiftUIPagerExample/Examples/BizarreExampleView.swift @@ -38,7 +38,7 @@ struct BizarreExampleView: View { } .itemSpacing(10) .horizontal(.rightToLeft) - .interactive(0.8) + .interactive(scale: 0.8) .itemAspectRatio(0.7) .background(Color.gray.opacity(0.5)) } diff --git a/Example/SwiftUIPagerExample/Examples/EmbeddedExampleView.swift b/Example/SwiftUIPagerExample/Examples/EmbeddedExampleView.swift index 373d227..88c9078 100644 --- a/Example/SwiftUIPagerExample/Examples/EmbeddedExampleView.swift +++ b/Example/SwiftUIPagerExample/Examples/EmbeddedExampleView.swift @@ -27,7 +27,9 @@ struct EmbeddedExampleView: View { id: \.self) { page in self.pageView(page) } - .rotation3D() + .interactive(rotation: true) + .interactive(scale: 0.7) + .interactive(opacity: 0.5) .itemSpacing(10) .itemAspectRatio(0.8, alignment: .end) .padding(8) diff --git a/README.md b/README.md index 4dc8b92..49bb862 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Create vertical or horizontal pagers, align the cards, change the direction of t - [Animations](Documentation/Usage.md#animations) - [Scale](Documentation/Usage.md#scale) - [Rotation](Documentation/Usage.md#rotation) + - [Opacity](Documentation/Usage.md#opacity) - [Loop](Documentation/Usage.md#loop) - [Page Transitions](Documentation/Usage.md#page-transitions) - [Add pages on demand](Documentation/Usage.md#add-pages-on-demand) diff --git a/Sources/SwiftUIPager/Pager+Buildable.swift b/Sources/SwiftUIPager/Pager+Buildable.swift index 016fa33..05ad878 100644 --- a/Sources/SwiftUIPager/Pager+Buildable.swift +++ b/Sources/SwiftUIPager/Pager+Buildable.swift @@ -168,33 +168,49 @@ extension Pager: Buildable { /// /// - Parameter scale: shrink ratio /// - Note: `scale` must be lower than _1_ and greather than _0_, otherwise it defaults to the previous value + @available(*, deprecated, renamed: "interactive(scale:)") public func interactive(_ scale: CGFloat) -> Self { guard !shouldRotate else { return self } - guard scale > 0, scale < 1 else { return self } - return mutating(keyPath: \.interactiveScale, value: scale) + return interactive(scale: scale) + } + + /// Call this method to provide a shrink ratio that will apply to the items that are not focused. + /// + /// - Parameter ratio: shrink ratio applied to unfocsed items + /// - Note: `ratio` must be lower than _1_ and greather than _0_, otherwise it defaults to the previous value + public func interactive(scale ratio: CGFloat) -> Self { + return mutating(keyPath: \.interactiveScale, value: max(0, min(1, ratio))) } /// Call this method to provide an interactive opacity effect to neighboring pages. The further they are /// from the focused page, the more opacity will be applied /// - /// - Parameter stepPercentage: opacity step increment between each index + /// - Parameter decrement: opacity step increment between each index /// /// For instance, if the focused index is _3_ and `stepPercentage` is `0.4`, /// then page _2_ and _4_ will have an opacity of `0.8`, pages _1_ and _5_ will have /// an opacity of `0.4` and so on. /// /// - Note: `increment` must be lower than _1_ and greather than _0_ - public func faded(_ stepPercentage: Double = 0.4) -> Self { - mutating(keyPath: \.opacityIncrement, value: stepPercentage) + public func interactive(opacity decrement: Double) -> Self { + mutating(keyPath: \.opacityIncrement, value: decrement) + } + + /// Call this method to add a 3D rotation effect. + /// + /// - Parameter value: `true` if the pages should have a 3D rotation effect + public func interactive(rotation shouldRotate: Bool) -> Self { + mutating(keyPath: \.shouldRotate, value: shouldRotate) } /// Call this method to add a 3D rotation effect. /// /// - Parameter value: `true` if the pages should have a 3D rotation effect /// - Note: If you call this method, any previous or later call to `interactive` will have no effect. + @available(*, deprecated, renamed: "interactive(rotation:)") public func rotation3D(_ value: Bool = true) -> Self { - mutating(keyPath: \.interactiveScale, value: value ? rotationInteractiveScale : 1) - .mutating(keyPath: \.shouldRotate, value: value) + interactive(scale: value ? 0.7 : 1) + .interactive(rotation: value) } /// Provides an increment to the page index offset. Use this to modify the scroll offset diff --git a/Sources/SwiftUIPager/Pager.swift b/Sources/SwiftUIPager/Pager.swift index 8cbf373..4f01178 100644 --- a/Sources/SwiftUIPager/Pager.swift +++ b/Sources/SwiftUIPager/Pager.swift @@ -15,11 +15,13 @@ import SwiftUI /// /// # Example # /// -/// Pager(page: $page -/// data: data, -/// content: { index in -/// self.pageView(index) -/// }).interactive(0.8) +/// Pager( +/// page: $page +/// data: data, +/// content: { index in +/// self.pageView(index) +/// }) +/// .interactive(scale: 0.8) /// .itemSpacing(10) /// .padding(30) /// .itemAspectRatio(0.6) @@ -45,9 +47,6 @@ public struct Pager: View where PageView: View, Element: /// Angle of rotation when should rotate let rotationDegrees: Double = 20 - /// Angle of rotation when should rotate - let rotationInteractiveScale: CGFloat = 0.7 - /// Axis of rotation when should rotate let rotationAxis: (x: CGFloat, y: CGFloat, z: CGFloat) = (0, 1, 0) @@ -189,8 +188,9 @@ public struct Pager: View where PageView: View, Element: .contentLoadingPolicy(contentLoadingPolicy) .loopPages(isInifinitePager, repeating: loopingCount) .alignment(alignment) - .interactive(interactiveScale) - .faded(opacityIncrement) + .interactive(scale: interactiveScale) + .interactive(opacity: opacityIncrement) + .interactive(rotation: shouldRotate) .pageOffset(pageOffset) .itemSpacing(itemSpacing) .itemAspectRatio(itemAspectRatio, alignment: itemAlignment) @@ -215,7 +215,6 @@ public struct Pager: View where PageView: View, Element: pagerContent = allowsMultiplePagination ? pagerContent.multiplePagination() : pagerContent pagerContent = isHorizontal ? pagerContent.horizontal(horizontalSwipeDirection) : pagerContent.vertical(verticalSwipeDirection) - pagerContent = shouldRotate ? pagerContent.rotation3D() : pagerContent if let preferredItemSize = preferredItemSize { pagerContent = pagerContent.preferredItemSize(preferredItemSize) diff --git a/Sources/SwiftUIPager/PagerContent+Buildable.swift b/Sources/SwiftUIPager/PagerContent+Buildable.swift index 7cdfdc8..dc18efc 100644 --- a/Sources/SwiftUIPager/PagerContent+Buildable.swift +++ b/Sources/SwiftUIPager/PagerContent+Buildable.swift @@ -164,32 +164,32 @@ extension Pager.PagerContent: Buildable { /// Call this method to provide a shrink ratio that will apply to the items that are not focused. /// - /// - Parameter scale: shrink ratio - /// - Note: `scale` must be lower than _1_ and greather than _0_, otherwise it defaults to the previous value - func interactive(_ scale: CGFloat) -> Self { - mutating(keyPath: \.interactiveScale, value: scale) + /// - Parameter ratio: shrink ratio applied to unfocsed items + /// - Note: `ratio` must be lower than _1_ and greather than _0_, otherwise it defaults to the previous value + func interactive(scale ratio: CGFloat) -> Self { + guard ratio > 0, ratio < 1 else { return self } + return mutating(keyPath: \.interactiveScale, value: ratio) } /// Call this method to provide an interactive opacity effect to neighboring pages. The further they are /// from the focused page, the more opacity will be applied /// - /// - Parameter stepPercentage: opacity step increment between each index + /// - Parameter decrement: opacity step increment between each index /// /// For instance, if the focused index is _3_ and `stepPercentage` is `0.4`, /// then page _2_ and _4_ will have an opacity of `0.8`, pages _1_ and _5_ will have /// an opacity of `0.4` and so on. /// /// - Note: `increment` must be lower than _1_ and greather than _0_ - func faded(_ stepPercentage: Double?) -> Self { - mutating(keyPath: \.opacityIncrement, value: stepPercentage) + func interactive(opacity decrement: Double?) -> Self { + mutating(keyPath: \.opacityIncrement, value: decrement) } - + /// Call this method to add a 3D rotation effect. /// /// - Parameter value: `true` if the pages should have a 3D rotation effect - /// - Note: If you call this method, any previous or later call to `interactive` will have no effect. - func rotation3D(_ value: Bool = true) -> Self { - mutating(keyPath: \.shouldRotate, value: value) + func interactive(rotation shouldRotate: Bool) -> Self { + mutating(keyPath: \.shouldRotate, value: shouldRotate) } /// Provides an increment to the page index offset. Use this to modify the scroll offset diff --git a/Sources/SwiftUIPager/PagerContent.swift b/Sources/SwiftUIPager/PagerContent.swift index be481d3..568804f 100644 --- a/Sources/SwiftUIPager/PagerContent.swift +++ b/Sources/SwiftUIPager/PagerContent.swift @@ -28,9 +28,6 @@ extension Pager { /// Angle of rotation when should rotate let rotationDegrees: Double = 20 - /// Angle of rotation when should rotate - let rotationInteractiveScale: CGFloat = 0.7 - /// Axis of rotation when should rotate let rotationAxis: (x: CGFloat, y: CGFloat, z: CGFloat) = (0, 1, 0) diff --git a/Tests/SwiftUIPagerTests/Pager+Buildable_Tests.swift b/Tests/SwiftUIPagerTests/Pager+Buildable_Tests.swift index 60f68fe..08e42be 100644 --- a/Tests/SwiftUIPagerTests/Pager+Buildable_Tests.swift +++ b/Tests/SwiftUIPagerTests/Pager+Buildable_Tests.swift @@ -48,7 +48,7 @@ final class Pager_Buildable_Tests: XCTestCase { func test_GivenPager_WhenFaded_ThenOpacityIncrementChanges() { var pager = givenPager - pager = pager.faded(0.2) + pager = pager.interactive(opacity: 0.2) let pagerContent = pager.content(for: CGSize(width: 100, height: 100)) XCTAssertEqual(pagerContent.opacityIncrement, 0.2) @@ -264,6 +264,17 @@ final class Pager_Buildable_Tests: XCTestCase { XCTAssertEqual(pagerContentInteractive.interactiveScale, pagerContentWithRotation.interactiveScale) XCTAssertEqual(pagerContentInteractive.shouldRotate, pagerContentWithRotation.shouldRotate) } + + func test_GivenPager_WhenCombineInteractiveModifier_ThenNoExclusive() { + let interactivePager = givenPager + .interactive(scale: 0.4) + .interactive(opacity: 0.3) + .interactive(rotation: true) + let pagerContent = interactivePager.content(for: CGSize(width: 100, height: 100)) + XCTAssertEqual(pagerContent.opacityIncrement, 0.3) + XCTAssertEqual(pagerContent.interactiveScale, 0.4) + XCTAssertTrue(pagerContent.shouldRotate) + } func test_GivenPager_When3DRotation_ThenShouldRotate() { var pager = givenPager @@ -271,7 +282,7 @@ final class Pager_Buildable_Tests: XCTestCase { let pagerContent = pager.content(for: CGSize(width: 100, height: 100)) XCTAssertTrue(pagerContent.shouldRotate) - XCTAssertEqual(pagerContent.interactiveScale, pagerContent.rotationInteractiveScale) + XCTAssertEqual(pagerContent.interactiveScale, 0.7) } func test_GivenPagerWith3DRotation_When3DRotationFalse_ThenShouldRotateFalse() { @@ -643,7 +654,9 @@ final class Pager_Buildable_Tests: XCTestCase { ("test_GivenPager_WhenOnDraggingChanged_ThenCallback", test_GivenPager_WhenOnDraggingChanged_ThenCallback), ("test_GivenPager_WhenOnDraggingEnded_ThenCallback", test_GivenPager_WhenOnDraggingEnded_ThenCallback), ("test_GivenPager_WhenOnPageChanged_ThenCallbackNotNil", test_GivenPager_WhenOnPageChanged_ThenCallbackNotNil), - ("test_GivenPager_WhenFaded_ThenOpacityIncrementChanges", test_GivenPager_WhenFaded_ThenOpacityIncrementChanges) + ("test_GivenPager_WhenFaded_ThenOpacityIncrementChanges", test_GivenPager_WhenFaded_ThenOpacityIncrementChanges), + ("test_GivenPager_WhenCombineInteractiveModifier_ThenNoExclusive", test_GivenPager_WhenCombineInteractiveModifier_ThenNoExclusive), + ("test_GivenPager_WhenCombineInteractiveModifier_ThenNoExclusive", test_GivenPager_WhenCombineInteractiveModifier_ThenNoExclusive) ] } diff --git a/Tests/SwiftUIPagerTests/PagerContent+Helper_Tests.swift b/Tests/SwiftUIPagerTests/PagerContent+Helper_Tests.swift index 9c4b9e7..153cfcb 100644 --- a/Tests/SwiftUIPagerTests/PagerContent+Helper_Tests.swift +++ b/Tests/SwiftUIPagerTests/PagerContent+Helper_Tests.swift @@ -26,18 +26,37 @@ final class PagerContent_Helper_Tests: XCTestCase { } func test_GivenPager_WhenOpacityForNotFoundIndex_ThenOne() { - XCTAssertEqual(givenPager.opacity(for: PageWrapper(batchId: 0, keyPath: \.self, element: -1)), 1) + let pager = givenPager.interactive(opacity: 0.3) + XCTAssertEqual(pager.opacity(for: PageWrapper(batchId: 0, keyPath: \.self, element: -1)), 1) } func test_GivenFadedPager_WhenOpacityForNotFoundIndex_ThenValues() { - let pagerContent = givenPager.faded(0.3) + let pagerContent = givenPager.interactive(opacity: 0.3) + pagerContent.pagerModel.index = 2 - let focusedItem = PageWrapper(batchId: 1, keyPath: \.self, element: 0) + let focusedItem = PageWrapper(batchId: 1, keyPath: \.self, element: 2) let neighbor1 = PageWrapper(batchId: 1, keyPath: \.self, element: 1) - let neighbor2 = PageWrapper(batchId: 1, keyPath: \.self, element: 2) + let neighbor2 = PageWrapper(batchId: 1, keyPath: \.self, element: 3) + let neighbor3 = PageWrapper(batchId: 1, keyPath: \.self, element: 4) XCTAssertEqual(pagerContent.opacity(for: focusedItem), 1) - XCTAssertEqual(pagerContent.opacity(for: neighbor1), 0.7) - XCTAssertEqual(pagerContent.opacity(for: neighbor2), 0.4) + XCTAssertEqual(Int((pagerContent.opacity(for: neighbor1) * 10).rounded()), 7) + XCTAssertEqual(Int((pagerContent.opacity(for: neighbor2) * 10).rounded()), 7) + XCTAssertEqual(Int((pagerContent.opacity(for: neighbor3) * 10).rounded()), 4) + } + + func test_GivenFadedPagerMovingForward_WhenOpacityForNotFoundIndex_ThenValues() { + let pagerContent = givenPager.interactive(opacity: 0.4) + pagerContent.pagerModel.index = 2 + pagerContent.pagerModel.draggingOffset = -150 + + let focusedItem = PageWrapper(batchId: 1, keyPath: \.self, element: 2) + let neighbor1 = PageWrapper(batchId: 1, keyPath: \.self, element: 1) + let neighbor2 = PageWrapper(batchId: 1, keyPath: \.self, element: 3) + let neighbor3 = PageWrapper(batchId: 1, keyPath: \.self, element: 4) + XCTAssertEqual(Int((pagerContent.opacity(for: focusedItem) * 10).rounded()), 8) + XCTAssertEqual(Int((pagerContent.opacity(for: neighbor1) * 10).rounded()), 4) + XCTAssertEqual(Int((pagerContent.opacity(for: neighbor2) * 10).rounded()), 8) + XCTAssertEqual(Int((pagerContent.opacity(for: neighbor3) * 10).rounded()), 4) } func test_GivenMultiplePaginationPager_WhenDragResult_ThenValues() { @@ -94,9 +113,9 @@ final class PagerContent_Helper_Tests: XCTestCase { } func test_GivenInteractivePager_WhenScaleIncrement_ThenInteractiveScaleInverse() { - let pager = givenPager.interactive(0.7) + let pager = givenPager.interactive(scale: 0.7) let scaleIncrement = pager.scaleIncrement - XCTAssertEqual(Int(scaleIncrement * 10), 3) + XCTAssertEqual(Int((scaleIncrement * 10).rounded()), 3) } func test_GivenPager_WhenNumberOfPages_ThenDataCount() { @@ -113,7 +132,7 @@ final class PagerContent_Helper_Tests: XCTestCase { } func test_GivenPagerWithRotation_WhenAxisForItem_ThenRotationAxis() { - let pager = givenPager.rotation3D() + let pager = givenPager.interactive(rotation: true) let (x, y, z) = pager.axis XCTAssertEqual(x, pager.rotationAxis.x) XCTAssertEqual(y, pager.rotationAxis.y) @@ -121,49 +140,49 @@ final class PagerContent_Helper_Tests: XCTestCase { } func test_GivenPager_WhenScaleForItem_Then1() { - let pager = givenPager.interactive(0.7) + let pager = givenPager.interactive(scale: 0.7) let item = PageWrapper(batchId: 1, keyPath: \.self, element: 0) let scale = pager.scale(for: item) XCTAssertEqual(scale, 1) } func test_GivenPager_WhenScaleForItem_ThenExpectedValue() { - let pager = givenPager.interactive(0.7) + let pager = givenPager.interactive(scale: 0.7) let item = PageWrapper(batchId: 1, keyPath: \.self, element: 1) let scale = pager.scale(for: item) XCTAssertEqual(scale, 0.7) } func test_GivenPagerDragging_WhenScaleForItem_ThenLessThanOne() { - let pager = givenPager.interactive(0.7).pageOffset(0.1) + let pager = givenPager.interactive(scale: 0.7).pageOffset(0.1) let item = PageWrapper(batchId: 1, keyPath: \.self, element: 1) let scale = pager.scale(for: item) XCTAssertLessThan(scale, 1) } func test_GivenPagerDragging_WhenScaleForItem_ThenGreaterThanInteractive() { - let pager = givenPager.interactive(0.7).pageOffset(0.1) + let pager = givenPager.interactive(scale: 0.7).pageOffset(0.1) let item = PageWrapper(batchId: 1, keyPath: \.self, element: 1) let scale = pager.scale(for: item) XCTAssertGreaterThan(scale, 0.7) } func test_GivenPagerDragging_WhenScaleForNonExistingItem_ThenInteractiveScale() { - let pager = givenPager.interactive(0.7).pageOffset(0.1) + let pager = givenPager.interactive(scale: 0.7).pageOffset(0.1) let item = PageWrapper(batchId: 1, keyPath: \.self, element: 200) let scale = pager.scale(for: item) XCTAssertEqual(scale, 0.7) } func test_GivenPagerDragging_WhenScaleForFarItem_ThenInteractiveScale() { - let pager = givenPager.interactive(0.7).pageOffset(-0.1) + let pager = givenPager.interactive(scale: 0.7).pageOffset(-0.1) let item = PageWrapper(batchId: 1, keyPath: \.self, element: 3) let scale = pager.scale(for: item) XCTAssertEqual(scale, 0.7) } func test_GivenPagerDragging_WhenScaleBouncing_ThenLessThanZero() { - let pager = givenPager.interactive(0.7).pageOffset(-0.1) + let pager = givenPager.interactive(scale: 0.7).pageOffset(-0.1) let item = PageWrapper(batchId: 1, keyPath: \.self, element: 1) let scale = pager.scale(for: item) XCTAssertLessThan(scale, 1) @@ -314,19 +333,19 @@ final class PagerContent_Helper_Tests: XCTestCase { } func test_GivenPagerWithRotation3D_WhenAngle_ThenZero() { - let pager = givenPager.rotation3D() + let pager = givenPager.interactive(rotation: true) let angle = pager.angle(for: .init(batchId: 1, keyPath: \.self, element: 0)) XCTAssertEqual(angle, .zero) } func test_GivenPagerWithRotation3DDraggingForward_WhenAngle_ThenGreaterThanZero() { - let pager = givenPager.rotation3D().pageOffset(1.1) + let pager = givenPager.interactive(rotation: true).pageOffset(1.1) let angle = pager.angle(for: .init(batchId: 1, keyPath: \.self, element: 0)) XCTAssertGreaterThan(angle.degrees, .zero) } func test_GivenPagerWithRotation3DDraggingBackward_WhenAngle_ThenLessThanZero() { - let pager = givenPager.rotation3D().pageOffset(-0.1) + let pager = givenPager.interactive(rotation: true).pageOffset(-0.1) let angle = pager.angle(for: .init(batchId: 1, keyPath: \.self, element: 0)) XCTAssertLessThan(angle.degrees, .zero) } @@ -391,6 +410,7 @@ final class PagerContent_Helper_Tests: XCTestCase { ("test_GivenInfinitePager_WhenDragLeft_ThenLastPage", test_GivenInfinitePager_WhenDragLeft_ThenLastPage), ("test_GivenPager_WhenOpacity_ThenOne", test_GivenPager_WhenOpacity_ThenOne), ("test_GivenPager_WhenOpacityForNotFoundIndex_ThenOne", test_GivenPager_WhenOpacityForNotFoundIndex_ThenOne), - ("test_GivenFadedPager_WhenOpacityForNotFoundIndex_ThenValues", test_GivenFadedPager_WhenOpacityForNotFoundIndex_ThenValues) + ("test_GivenFadedPager_WhenOpacityForNotFoundIndex_ThenValues", test_GivenFadedPager_WhenOpacityForNotFoundIndex_ThenValues), + ("test_GivenFadedPagerMovingForward_WhenOpacityForNotFoundIndex_ThenValues", test_GivenFadedPagerMovingForward_WhenOpacityForNotFoundIndex_ThenValues) ] }