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/HPageView.swift b/Sources/SwiftUIPageView/API/HPageView.swift index a4889cb..3c16a5a 100644 --- a/Sources/SwiftUIPageView/API/HPageView.swift +++ b/Sources/SwiftUIPageView/API/HPageView.swift @@ -29,12 +29,14 @@ where Content : View public init(alignment: PageAlignment = .center, pageWidth: CGFloat? = nil, spacing: CGFloat? = nil, + index: Binding = Binding.constant(0), @ViewBuilder content: @escaping () -> Content) { body = PageView(alignment: alignment.alignment, axis: .horizontal, content: content, pageLength: pageWidth, - spacing: spacing) + spacing: spacing, + index: index) } } diff --git a/Sources/SwiftUIPageView/API/PageView.swift b/Sources/SwiftUIPageView/API/PageView.swift index c8455b9..b15fdad 100644 --- a/Sources/SwiftUIPageView/API/PageView.swift +++ b/Sources/SwiftUIPageView/API/PageView.swift @@ -26,7 +26,8 @@ where Content : View content: content, pageLength: pageLength, spacing: spacing, - viewLength: viewLength) + viewLength: viewLength, + index: $index) } .animation(nil, value: axis) } @@ -36,6 +37,7 @@ where Content : View internal var content: () -> Content internal var pageLength: CGFloat? internal var spacing: CGFloat? + @Binding var index: Int } @@ -62,6 +64,7 @@ public extension PageView { alignment: PageAlignment = .center, pageLength: CGFloat? = nil, spacing: CGFloat? = nil, + index: Binding = Binding.constant(0), @ViewBuilder content: @escaping () -> Content) { self.alignment = alignment.alignment @@ -69,5 +72,6 @@ public extension PageView { self.content = content self.pageLength = pageLength self.spacing = spacing + self._index = index } } diff --git a/Sources/SwiftUIPageView/API/VPageView.swift b/Sources/SwiftUIPageView/API/VPageView.swift index 37471eb..709927a 100644 --- a/Sources/SwiftUIPageView/API/VPageView.swift +++ b/Sources/SwiftUIPageView/API/VPageView.swift @@ -29,12 +29,14 @@ where Content : View public init(alignment: PageAlignment = .center, pageHeight: CGFloat? = nil, spacing: CGFloat? = nil, + index: Binding = Binding.constant(0), @ViewBuilder content: @escaping () -> Content) { body = PageView(alignment: alignment.alignment, axis: .vertical, content: content, pageLength: pageHeight, - spacing: spacing) + spacing: spacing, + index: index) } } diff --git a/Sources/SwiftUIPageView/Internal/PageGestureView.swift b/Sources/SwiftUIPageView/Internal/PageGestureView.swift index 34b5ce2..2a8902b 100644 --- a/Sources/SwiftUIPageView/Internal/PageGestureView.swift +++ b/Sources/SwiftUIPageView/Internal/PageGestureView.swift @@ -21,6 +21,7 @@ where Content : View var pageLength: CGFloat var spacing: CGFloat var viewLength: CGFloat + @Binding var index: Int var body: some View { PageLayoutView(alignment: alignment, @@ -100,6 +101,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 +145,7 @@ where Content : View withAnimation(animationState.dragAnimation) { pageState.index = newIndex pageState.indexOffset = 0 + self.index = intFromIndex(newIndex) } } } @@ -167,6 +182,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 +217,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 +246,6 @@ where Content : View pageState.dragState = distance == 0 ? .ended : .ending pageState.index = newIndex pageState.indexOffset = 0 + self.index = intFromIndex(newIndex) } }