From 3b05829020ec5e10a0997455b2a7fb18dfb4af53 Mon Sep 17 00:00:00 2001 From: Fernando Moya de Rivas Date: Thu, 23 Jan 2020 15:18:51 +0000 Subject: [PATCH] Fixing gesture collisions --- .../Helpers/SizeViewModifier.swift | 13 -------- Sources/SwiftUIPager/Pager+Buildable.swift | 5 +++ Sources/SwiftUIPager/Pager+Helper.swift | 10 +++--- Sources/SwiftUIPager/Pager.swift | 32 ++++++++++++------- 4 files changed, 31 insertions(+), 29 deletions(-) diff --git a/Sources/SwiftUIPager/Helpers/SizeViewModifier.swift b/Sources/SwiftUIPager/Helpers/SizeViewModifier.swift index 50cbbc4..d678099 100644 --- a/Sources/SwiftUIPager/Helpers/SizeViewModifier.swift +++ b/Sources/SwiftUIPager/Helpers/SizeViewModifier.swift @@ -8,17 +8,6 @@ import SwiftUI -/// Tracks the size of the view -struct SizePreferenceKey: PreferenceKey { - static var defaultValue: CGSize = .zero - - static func reduce(value: inout CGSize, nextValue: () -> CGSize) { - let newValue = nextValue() - guard value != newValue else { return } - value = newValue - } -} - /// This modifier wraps a view into a `GeometryReader` and tracks the available space by using `SizePreferenceKey` on the content struct SizeViewModifier: ViewModifier { @@ -27,8 +16,6 @@ struct SizeViewModifier: ViewModifier { func body(content: Content) -> some View { GeometryReader { proxy in content - .preference(key: SizePreferenceKey.self, - value: proxy.size) .frame(width: proxy.size.width, height: proxy.size.height) .onAppear (perform: { self.size = proxy.size diff --git a/Sources/SwiftUIPager/Pager+Buildable.swift b/Sources/SwiftUIPager/Pager+Buildable.swift index 68523a1..de7397b 100644 --- a/Sources/SwiftUIPager/Pager+Buildable.swift +++ b/Sources/SwiftUIPager/Pager+Buildable.swift @@ -10,6 +10,11 @@ import SwiftUI extension Pager: Buildable { + /// Adds a `TapGesture` to the items to bring them to focus + public func itemTappable(_ value: Bool) -> Self { + mutating(keyPath: \.isItemTappable, value: value) + } + /// Returns a horizontal pager public func horizontal() -> Self { mutating(keyPath: \.isHorizontal, value: true) diff --git a/Sources/SwiftUIPager/Pager+Helper.swift b/Sources/SwiftUIPager/Pager+Helper.swift index 1a5038d..6a2bd5b 100644 --- a/Sources/SwiftUIPager/Pager+Helper.swift +++ b/Sources/SwiftUIPager/Pager+Helper.swift @@ -93,7 +93,7 @@ extension Pager { } /// Data that is being displayed at the moment - var dataDisplayed: [Data] { + var dataDisplayed: [Element] { Array(data[lowerPageDisplayed.. Angle { + func angle(for item: Element) -> Angle { guard shouldRotate else { return .zero } guard let index = data.firstIndex(of: item) else { return .zero } @@ -133,7 +133,7 @@ extension Pager { } /// Axis for the rotations effect - func axis(for item: Data) -> (CGFloat, CGFloat, CGFloat) { + func axis(for item: Element) -> (CGFloat, CGFloat, CGFloat) { guard shouldRotate else { return (0, 0, 0) } guard let index = data.firstIndex(of: item) else { return (0, 0, 0) } @@ -142,7 +142,7 @@ extension Pager { } /// Scale that applies to a particular item - func scale(for item: Data) -> CGFloat { + func scale(for item: Element) -> CGFloat { guard isDragging else { return isFocused(item) ? 1 : interactiveScale } let totalIncrement = abs(totalOffset / pageDistance) @@ -164,7 +164,7 @@ extension Pager { } /// Returns true if the item is focused on the screen. - func isFocused(_ item: Data) -> Bool { + func isFocused(_ item: Element) -> Bool { data.firstIndex(of: item) == currentPage } diff --git a/Sources/SwiftUIPager/Pager.swift b/Sources/SwiftUIPager/Pager.swift index 540e658..81d840e 100644 --- a/Sources/SwiftUIPager/Pager.swift +++ b/Sources/SwiftUIPager/Pager.swift @@ -29,7 +29,7 @@ import SwiftUI /// - 30 px of vertical insets /// - 0.6 shrink ratio for items that aren't focused. /// -public struct Pager: View where Content: View, Data: Identifiable & Equatable { +public struct Pager: View where PageView: View, Element: Identifiable & Equatable { /// `Direction` determines the direction of the swipe gesture enum Direction { @@ -58,13 +58,17 @@ public struct Pager: View where Content: View, Data: Identifiabl /*** Dependencies ***/ /// `ViewBuilder` block to create each page - let content: (Data) -> Content + let content: (Element) -> PageView /// Array of items that will populate each page - var data: [Data] + var data: [Element] /*** ViewModified properties ***/ + /// `true` if items are tapable + var isItemTappable: Bool = false + + /// `true` if the pager is horizontal var isHorizontal: Bool = true /// Shrink ratio that affects the items that aren't focused @@ -111,12 +115,12 @@ public struct Pager: View where Content: View, Data: Identifiabl /// - Parameter page: Binding to the index of the focused page /// - Parameter data: Array of items to populate the content /// - Parameter content: Factory method to build new pages - public init(page: Binding, data: [Data], @ViewBuilder content: @escaping (Data) -> Content) { + public init(page: Binding, data: [Element], @ViewBuilder content: @escaping (Element) -> PageView) { self._page = page self.data = data self.content = content } - + public var body: some View { HStack(spacing: self.interactiveItemSpacing) { ForEach(self.dataDisplayed) { item in @@ -127,11 +131,8 @@ public struct Pager: View where Content: View, Data: Identifiabl axis: (0, 0, 1)) .rotation3DEffect(self.angle(for: item), axis: self.axis(for: item)) - .onTapGesture (perform: { - withAnimation(.spring()) { - self.scrollToItem(item) - } - }) + .gesture(self.tapGesture(for: item)) + .disabled(self.isFocused(item) || !self.isItemTappable) } .offset(x: self.xOffset, y : 0) } @@ -147,11 +148,20 @@ public struct Pager: View where Content: View, Data: Identifiabl extension Pager { /// Helper function to scroll to a specific item. - func scrollToItem(_ item: Data) { + func scrollToItem(_ item: Element) { guard let index = data.firstIndex(of: item) else { return } self.page = index } + func tapGesture(for item: Element) -> some Gesture { + TapGesture(count: 1) + .onEnded({ _ in + withAnimation(.spring()) { + self.scrollToItem(item) + } + }) + } + /// `DragGesture` customized to work with `Pager` var swipeGesture: some Gesture { DragGesture()