diff --git a/Package.swift b/Package.swift index 4a579f8..8a05b12 100644 --- a/Package.swift +++ b/Package.swift @@ -6,7 +6,8 @@ let package = Package( name: "SwiftUIPageView", platforms: [ .iOS(.v14), - .watchOS(.v7) + .watchOS(.v7), + .macOS(.v11), ], products: [ .library( diff --git a/README.md b/README.md index 5280e19..ac61fb2 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ HPageView(alignment: .leading, pageWidth: 250, spacing: 12) { * `pageWidth`: The width of each page, or `nil` if you want each page to fill the width of the page view. * `spacing`: The distance between adjacent pages, or `nil` if you want the page view to choose a default distance for each pair of pages. * `content`: A view builder that creates the content of this page view. +* `index`: An `Int` binding that exposes active page ## VPageView A view that arranges its children in a vertical line, and provides paged scrolling behaviour. @@ -35,6 +36,7 @@ VPageView(alignment: .top, pageHeight: 250, spacing: 12) { * `pageHeight`: The height of each page, or `nil` if you want each page to fill the height of the page view. * `spacing`: The distance between adjacent pages, or `nil` if you want the page view to choose a default distance for each pair of pages. * `content`: A view builder that creates the content of this page view. +* `index`: An `Int` binding that exposes active page ## PageView A view that arranges its children in a line, and provides paged scrolling behaviour. @@ -54,6 +56,35 @@ PageView(.horizontal, alignment: .leading, pageLength: 250, spacing: 12) { * `pageLength`: The length of each page, parallel to the layout axis, or `nil` if you want each page to fill the length of the page view. * `spacing`: The distance between adjacent pages, or `nil` if you want the page view to choose a default distance for each pair of pages. * `content`: A view builder that creates the content of this page view. +* `index`: An `Int` binding that exposes active page + +## Index binding example +``` +@State var currentIndex: Int = 0 + +... + +/// inside `body`: +ZStack(alignment: .bottom) { + HPageView(alignment: .leading, pageWidth: 250, spacing: 12, index: $currentIndex) { + ForEach(items, id: \.id) { item in + ItemView(item: item) + .padding(.bottom, 24) + } + } + + /// Indicator dots + HStack(spacing: 8) { + ForEach(items.indices, id: \.self) { index in + Circle() + .fill(currentIndex == index ? Color.primary : Color.secondary) + .frame(width: 8, height: 8) + .animation(.spring(), value: currentIndex == index) + } + } + .padding(.bottom, 8) +} +``` ## PageViewReader A view that provides programmatic paging, by working with a proxy to move to child pages. diff --git a/Sources/SwiftUIPageView/API/GestureMinimumDistance.swift b/Sources/SwiftUIPageView/API/GestureMinimumDistance.swift new file mode 100644 index 0000000..72e2c28 --- /dev/null +++ b/Sources/SwiftUIPageView/API/GestureMinimumDistance.swift @@ -0,0 +1,27 @@ +// +// File.swift +// +// +// Created by Tomas Kafka on 27.06.2022. +// + +import Foundation +import CoreGraphics + +public enum GestureMinimumDistance { + /// old default + case compatible + case comfortable + case custom(CGFloat) + + var value: CGFloat { + switch self { + case .compatible: + return 15 + case .comfortable: + return 31 + case .custom(let customDistance): + return customDistance + } + } +} diff --git a/Sources/SwiftUIPageView/API/HPageView.swift b/Sources/SwiftUIPageView/API/HPageView.swift index a4889cb..31db54c 100644 --- a/Sources/SwiftUIPageView/API/HPageView.swift +++ b/Sources/SwiftUIPageView/API/HPageView.swift @@ -29,12 +29,16 @@ where Content : View public init(alignment: PageAlignment = .center, pageWidth: CGFloat? = nil, spacing: CGFloat? = nil, + gestureMinimumDistance: GestureMinimumDistance = .compatible, + index: Binding = Binding.constant(0), @ViewBuilder content: @escaping () -> Content) { body = PageView(alignment: alignment.alignment, axis: .horizontal, content: content, pageLength: pageWidth, - spacing: spacing) + spacing: spacing, + gestureMinimumDistance: gestureMinimumDistance, + index: index) } } diff --git a/Sources/SwiftUIPageView/API/PageView.swift b/Sources/SwiftUIPageView/API/PageView.swift index c8455b9..45645af 100644 --- a/Sources/SwiftUIPageView/API/PageView.swift +++ b/Sources/SwiftUIPageView/API/PageView.swift @@ -26,7 +26,9 @@ where Content : View content: content, pageLength: pageLength, spacing: spacing, - viewLength: viewLength) + gestureMinimumDistance: gestureMinimumDistance, + viewLength: viewLength, + index: $index) } .animation(nil, value: axis) } @@ -36,6 +38,8 @@ where Content : View internal var content: () -> Content internal var pageLength: CGFloat? internal var spacing: CGFloat? + internal var gestureMinimumDistance: GestureMinimumDistance + @Binding var index: Int } @@ -62,6 +66,8 @@ public extension PageView { alignment: PageAlignment = .center, pageLength: CGFloat? = nil, spacing: CGFloat? = nil, + gestureMinimumDistance: GestureMinimumDistance, + index: Binding = Binding.constant(0), @ViewBuilder content: @escaping () -> Content) { self.alignment = alignment.alignment @@ -69,5 +75,7 @@ public extension PageView { self.content = content self.pageLength = pageLength self.spacing = spacing + self.gestureMinimumDistance = gestureMinimumDistance + self._index = index } } diff --git a/Sources/SwiftUIPageView/API/VPageView.swift b/Sources/SwiftUIPageView/API/VPageView.swift index 37471eb..63d5e98 100644 --- a/Sources/SwiftUIPageView/API/VPageView.swift +++ b/Sources/SwiftUIPageView/API/VPageView.swift @@ -29,12 +29,16 @@ where Content : View public init(alignment: PageAlignment = .center, pageHeight: CGFloat? = nil, spacing: CGFloat? = nil, + index: Binding = Binding.constant(0), + gestureMinimumDistance: GestureMinimumDistance = .compatible, @ViewBuilder content: @escaping () -> Content) { body = PageView(alignment: alignment.alignment, axis: .vertical, content: content, pageLength: pageHeight, - spacing: spacing) + spacing: spacing, + gestureMinimumDistance: gestureMinimumDistance, + index: index) } } diff --git a/Sources/SwiftUIPageView/Internal/PageGestureView.swift b/Sources/SwiftUIPageView/Internal/PageGestureView.swift index 34b5ce2..b72771e 100644 --- a/Sources/SwiftUIPageView/Internal/PageGestureView.swift +++ b/Sources/SwiftUIPageView/Internal/PageGestureView.swift @@ -20,7 +20,9 @@ where Content : View var content: () -> Content var pageLength: CGFloat var spacing: CGFloat + var gestureMinimumDistance: GestureMinimumDistance var viewLength: CGFloat + @Binding var index: Int var body: some View { PageLayoutView(alignment: alignment, @@ -55,7 +57,7 @@ where Content : View let minimumDistance: CGFloat switch pageState.dragState { - case .dragging, .nearlyEnded, .ended: minimumDistance = 15 + case .dragging, .nearlyEnded, .ended: minimumDistance = gestureMinimumDistance.value case .ending: minimumDistance = 0 } @@ -100,6 +102,19 @@ where Content : View private func offsetToIndex(_ offset: CGFloat) -> CGFloat { -offset / (pageLength + spacing) } + + private func intFromIndex(_ index: CGFloat) -> Int { + if index == .infinity { + return pageState.viewCount - 1 + } else if index == -.infinity { + return 0 + } else if index.isNaN { + return 0 + } else { + return Int(round(index)) + } + } + private func onDragChanged(value: DragGesture.Value) { if let initialIndex = pageState.initialIndex { onDragUpdated(value: value, initialIndex: initialIndex) @@ -131,6 +146,7 @@ where Content : View withAnimation(animationState.dragAnimation) { pageState.index = newIndex pageState.indexOffset = 0 + self.index = intFromIndex(newIndex) } } } @@ -167,6 +183,7 @@ where Content : View withAnimation(animationState.dragAnimation) { pageState.index = newIndex pageState.indexOffset = 0 + self.index = intFromIndex(newIndex) } } private func onDragStarted(value: DragGesture.Value) { @@ -201,6 +218,7 @@ where Content : View withAnimation(animationState.dragAnimation) { pageState.index = offsetToIndex(offset) pageState.indexOffset = offsetToIndex(additionalOffset - initialOffset) + self.index = intFromIndex(offsetToIndex(offset)) } } private func onDragUpdated(value: DragGesture.Value, initialIndex: CGFloat) { @@ -229,5 +247,6 @@ where Content : View pageState.dragState = distance == 0 ? .ended : .ending pageState.index = newIndex pageState.indexOffset = 0 + self.index = intFromIndex(newIndex) } }