From b5693114cd60156c32f4d990222a1701f0134b1a Mon Sep 17 00:00:00 2001 From: Kacper Raczy Date: Thu, 18 Jun 2020 12:49:17 +0200 Subject: [PATCH 1/5] Removed contentOffset --- Examples/PageViewDemo/ContentView.swift | 2 +- Sources/PageContent.swift | 4 ++-- Sources/PageScrollState.swift | 32 ++++++++----------------- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/Examples/PageViewDemo/ContentView.swift b/Examples/PageViewDemo/ContentView.swift index 92e5d70..051d047 100644 --- a/Examples/PageViewDemo/ContentView.swift +++ b/Examples/PageViewDemo/ContentView.swift @@ -11,7 +11,7 @@ import SwiftUI struct ContentView: View { var body: some View { - HPageView { + VPageView { CustomView() CustomListView() CustomView() diff --git a/Sources/PageContent.swift b/Sources/PageContent.swift index 258a8d8..f9eed86 100644 --- a/Sources/PageContent.swift +++ b/Sources/PageContent.swift @@ -92,7 +92,7 @@ struct PageContent: View where Stack: View, Control: View { private func horizontalOffset(using geometry: GeometryProxy) -> CGFloat { if state.isGestureActive { - return baseOffset + state.contentOffset + return baseOffset + -1 * CGFloat(state.selectedPage) * geometry.size.width + state.pageOffset } else { return baseOffset + -1 * CGFloat(state.selectedPage) * geometry.size.width } @@ -100,7 +100,7 @@ struct PageContent: View where Stack: View, Control: View { private func verticalOffset(using geometry: GeometryProxy) -> CGFloat { if state.isGestureActive { - return baseOffset + state.contentOffset + return baseOffset + -1 * CGFloat(state.selectedPage) * geometry.size.height + state.pageOffset } else { return baseOffset + -1 * CGFloat(state.selectedPage) * geometry.size.height } diff --git a/Sources/PageScrollState.swift b/Sources/PageScrollState.swift index 52f596b..4814995 100644 --- a/Sources/PageScrollState.swift +++ b/Sources/PageScrollState.swift @@ -9,17 +9,16 @@ import SwiftUI class PageScrollState: ObservableObject { @Published var selectedPage: Int = 0 - @Published var contentOffset: CGFloat = 0.0 + @Published var pageOffset: CGFloat = 0.0 @Published var isGestureActive: Bool = false - var scrollOffset: CGFloat = 0.0 func horizontalDragChanged(_ value: DragGesture.Value, viewCount: Int, pageWidth: CGFloat) { isGestureActive = true let delta = value.translation.width if (delta > 0 && selectedPage == 0) || (delta < 0 && selectedPage == viewCount - 1) { - contentOffset = scrollOffset + delta / 3.0 + pageOffset = delta / 3.0 } else { - contentOffset = scrollOffset + delta + pageOffset = delta } } @@ -31,9 +30,9 @@ class PageScrollState: ObservableObject { isGestureActive = true let delta = value.translation.height if (delta > 0 && selectedPage == 0) || (delta < 0 && selectedPage == viewCount - 1) { - contentOffset = scrollOffset + delta / 3.0 + pageOffset = delta / 3.0 } else { - contentOffset = scrollOffset + delta + pageOffset = delta } } @@ -42,27 +41,16 @@ class PageScrollState: ObservableObject { } private func dragEnded(_ value: DragGesture.Value, viewCount: Int, dimension: CGFloat) { - var newOffset = contentOffset var newPage = selectedPage - if contentOffset > 0 { - newOffset = 0 - } else if contentOffset < -(dimension * CGFloat(viewCount - 1)) { - newOffset = -(dimension * CGFloat(viewCount - 1)) - } else { - let pageOffset = abs(contentOffset) - CGFloat(selectedPage) * dimension - if pageOffset > 0.5*dimension { - newPage += 1 - } else if pageOffset < -0.5*dimension { - newPage -= 1 - } - - newOffset = -CGFloat(newPage) * dimension + if pageOffset > 0.5*dimension && selectedPage != 0 { + newPage -= 1 + } else if pageOffset < -0.5*dimension && selectedPage != viewCount - 1 { + newPage += 1 } withAnimation(.easeInOut(duration: 0.2)) { - self.contentOffset = newOffset + self.pageOffset = 0.0 self.selectedPage = newPage - self.scrollOffset = self.contentOffset } DispatchQueue.main.async { From 7131150e8197bfce33744d15a7a165762c2d3bb0 Mon Sep 17 00:00:00 2001 From: Kacper Raczy Date: Thu, 18 Jun 2020 16:38:01 +0200 Subject: [PATCH 2/5] Fixed issue with offset freezing while dragging --- .../ContentView.swift | 30 +++---- Examples/PageViewDemo/ContentView.swift | 17 +++- Examples/PageViewDemo/Views.swift | 14 +++- Sources/PageScrollState.swift | 36 ++++++++- Sources/PageView.swift | 80 ++++++++++++------- 5 files changed, 127 insertions(+), 50 deletions(-) diff --git a/Examples/PageViewDemo WatchKit Extension/ContentView.swift b/Examples/PageViewDemo WatchKit Extension/ContentView.swift index 516c919..f4e7aac 100644 --- a/Examples/PageViewDemo WatchKit Extension/ContentView.swift +++ b/Examples/PageViewDemo WatchKit Extension/ContentView.swift @@ -10,24 +10,26 @@ import SwiftUI //import PageView struct ContentView: View { + @State var selectedPage: Int = 0 + var body: some View { // Horizontal - HPageView { - CustomButtonView() - CustomButtonView() - CustomButtonView() - CustomView() - CustomListView() - CustomView() + HPageView(selectedPage: $selectedPage) { + CustomButtonView(pageIndex: 0) + CustomButtonView(pageIndex: 1) + CustomButtonView(pageIndex: 2) + CustomView(pageIndex: 3) + CustomListView(pageIndex: 4) + CustomView(pageIndex: 5) }.edgesIgnoringSafeArea(.init(arrayLiteral: .leading, .trailing, .bottom)) // Vertical -// VPageView { -// CustomButtonView() -// CustomButtonView() -// CustomButtonView() -// CustomView() -// CustomView() -// CustomView() +// VPageView(selectedPage: $selectedPage) { +// CustomButtonView(pageIndex: 0) +// CustomButtonView(pageIndex: 1) +// CustomButtonView(pageIndex: 2) +// CustomView(pageIndex: 3) +// CustomView(pageIndex: 4) +// CustomView(pageIndex: 5) // }.edgesIgnoringSafeArea(.init(arrayLiteral: .leading, .trailing, .bottom)) } } diff --git a/Examples/PageViewDemo/ContentView.swift b/Examples/PageViewDemo/ContentView.swift index 051d047..bf02fe1 100644 --- a/Examples/PageViewDemo/ContentView.swift +++ b/Examples/PageViewDemo/ContentView.swift @@ -10,12 +10,21 @@ import SwiftUI //import PageView struct ContentView: View { + @State var selectedPage = 2 + var body: some View { - VPageView { - CustomView() - CustomListView() - CustomView() + // horizontal + HPageView(selectedPage: $selectedPage) { + CustomView(pageIndex: 0) + CustomListView(pageIndex: 1) + CustomView(pageIndex: 2) } + // vertical +// VPageView(selectedPage: $selectedPage) { +// CustomView(pageIndex: 0) +// CustomView(pageIndex: 1) +// CustomView(pageIndex: 2) +// } } } diff --git a/Examples/PageViewDemo/Views.swift b/Examples/PageViewDemo/Views.swift index e9d5544..ad0258b 100644 --- a/Examples/PageViewDemo/Views.swift +++ b/Examples/PageViewDemo/Views.swift @@ -15,30 +15,34 @@ extension View { } struct CustomButtonView: View { + let pageIndex: Int + var body: some View { VStack { Button(action: { print("Button 1 tapped") }, label: { - Text("Button 1") + Text("Button 1 at \(pageIndex)") }) Button(action: { print("Button 2 tapped") }, label: { - Text("Button 2") + Text("Button 2 at \(pageIndex)") }) } } } struct CustomView: View { + let pageIndex: Int + var body: some View { VStack { Image(systemName: "globe").resizable() .scaledToFit() .frame(width: 50, height: 50) .foregroundColor(.orange) - Text("Hello world") + Text("Hello world: \(pageIndex)") .font(.system(size: 24)) .fontWeight(.bold) } @@ -46,12 +50,14 @@ struct CustomView: View { } struct CustomListView: View { + let pageIndex: Int + var body: some View { List(0..<10) { (i) in HStack { Text("Cell \(i)") Spacer() - Text("Detail") + Text("Page index: \(self.pageIndex)") } } } diff --git a/Sources/PageScrollState.swift b/Sources/PageScrollState.swift index 4814995..9f10020 100644 --- a/Sources/PageScrollState.swift +++ b/Sources/PageScrollState.swift @@ -8,10 +8,26 @@ import SwiftUI class PageScrollState: ObservableObject { - @Published var selectedPage: Int = 0 + + // MARK: Types + + struct TransactionInfo { + var dragValue: DragGesture.Value! + var geometryProxy: GeometryProxy! + } + + // MARK: Properties + + @Binding var selectedPage: Int @Published var pageOffset: CGFloat = 0.0 @Published var isGestureActive: Bool = false + init(selectedPageBinding: Binding) { + self._selectedPage = selectedPageBinding + } + + // MARK: DragGesture callbacks + func horizontalDragChanged(_ value: DragGesture.Value, viewCount: Int, pageWidth: CGFloat) { isGestureActive = true let delta = value.translation.width @@ -57,4 +73,22 @@ class PageScrollState: ObservableObject { self.isGestureActive = false } } + + // MARK: Gesture States + + func horizontalGestureState(pageCount: Int) -> GestureState { + return GestureState(initialValue: TransactionInfo()) { [weak self] (info, _) in + let width = info.geometryProxy.size.width + let dragValue = info.dragValue! + self?.horizontalDragEnded(dragValue, viewCount: pageCount, pageWidth: width) + } + } + + func verticalGestureState(pageCount: Int) -> GestureState { + return GestureState(initialValue: TransactionInfo()) { [weak self] (info, _) in + let height = info.geometryProxy.size.height + let dragValue = info.dragValue! + self?.verticalDragEnded(dragValue, viewCount: pageCount, pageHeight: height) + } + } } diff --git a/Sources/PageView.swift b/Sources/PageView.swift index 8f1c64b..c197dc8 100644 --- a/Sources/PageView.swift +++ b/Sources/PageView.swift @@ -13,15 +13,22 @@ public struct HPageView: View where Pages: View { public let pages: PageContainer public let pageCount: Int public let pageControlAlignment: Alignment + @GestureState var stateTransaction: PageScrollState.TransactionInfo - public init(theme: PageControlTheme = .default, @PageViewBuilder builder: () -> PageContainer) { - self.state = PageScrollState() + public init( + selectedPage: Binding, + theme: PageControlTheme = .default, + @PageViewBuilder builder: () -> PageContainer + ) { + let state = PageScrollState(selectedPageBinding: selectedPage) + self.state = state self.theme = theme let pages = builder() self.pages = pages self.pageCount = pages.count self.pageControlAlignment = theme.alignment ?? Alignment(horizontal: .center, vertical: .bottom) + self._stateTransaction = state.horizontalGestureState(pageCount: pages.count) } public var body: some View { @@ -41,19 +48,22 @@ public struct HPageView: View where Pages: View { pageControlBuilder: pageControlBuilder) .contentShape(Rectangle()) .highPriorityGesture(DragGesture(minimumDistance: 8.0) - .onChanged({ self.onDragChanged($0, geometry: geometry) }) - .onEnded({ self.onDragEnded($0, geometry: geometry) }) + .updating(self.$stateTransaction, body: { value, state, _ in + state.dragValue = value + state.geometryProxy = geometry + }) + .onChanged({ + let width = geometry.size.width + let pageCount = self.pageCount + self.state.horizontalDragChanged($0, viewCount: pageCount, pageWidth: width) + }) + /* + There is a bug, where onEnded is not called, when gesture is cancelled. + So onEnded is handled using reset handler in `GestureState` (look `PageScrollState`) + */ ) } } - - private func onDragChanged(_ value: DragGesture.Value, geometry: GeometryProxy) { - state.horizontalDragChanged(value, viewCount: pageCount, pageWidth: geometry.size.width) - } - - private func onDragEnded(_ value: DragGesture.Value, geometry: GeometryProxy) { - state.horizontalDragEnded(value, viewCount: pageCount, pageWidth: geometry.size.width) - } } public struct VPageView: View where Pages: View { @@ -62,15 +72,21 @@ public struct VPageView: View where Pages: View { public let pages: PageContainer public let pageCount: Int public let pageControlAlignment: Alignment + @GestureState var stateTransaction: PageScrollState.TransactionInfo - public init(theme: PageControlTheme = .default, @PageViewBuilder builder: () -> PageContainer) { - self.state = PageScrollState() + public init( + selectedPage: Binding, + theme: PageControlTheme = .default, + @PageViewBuilder builder: () -> PageContainer + ) { + self.state = PageScrollState(selectedPageBinding: selectedPage) self.theme = theme let pages = builder() self.pages = pages self.pageCount = pages.count self.pageControlAlignment = theme.alignment ?? Alignment(horizontal: .leading, vertical: .center) + self._stateTransaction = state.verticalGestureState(pageCount: pages.count) } public var body: some View { @@ -90,19 +106,22 @@ public struct VPageView: View where Pages: View { pageControlBuilder: pageControlBuilder) .contentShape(Rectangle()) .highPriorityGesture(DragGesture(minimumDistance: 8.0) - .onChanged({ self.onDragChanged($0, geometry: geometry) }) - .onEnded({ self.onDragEnded($0, geometry: geometry) }) + .updating(self.$stateTransaction, body: { value, state, _ in + state.dragValue = value + state.geometryProxy = geometry + }) + .onChanged({ + let height = geometry.size.height + let pageCount = self.pageCount + self.state.verticalDragChanged($0, viewCount: pageCount, pageHeight: height) + }) + /* + There is a bug, where onEnded is not called, when gesture is cancelled. + So onEnded is handled using reset handler in `GestureState`. (look `PageScrollState`) + */ ) } } - - private func onDragChanged(_ value: DragGesture.Value, geometry: GeometryProxy) { - state.verticalDragChanged(value, viewCount: pageCount, pageHeight: geometry.size.height) - } - - private func onDragEnded(_ value: DragGesture.Value, geometry: GeometryProxy) { - state.verticalDragEnded(value, viewCount: pageCount, pageHeight: geometry.size.height) - } } #if DEBUG @@ -117,7 +136,7 @@ struct PageView_Previews: PreviewProvider { .font(.system(size: 24)) .fontWeight(.bold) } - + let v2 = VStack { Image(systemName: "heart").resizable() .scaledToFit() @@ -128,12 +147,19 @@ struct PageView_Previews: PreviewProvider { .fontWeight(.bold) .foregroundColor(.gray) } - + var theme = PageControlTheme.default theme.alignment = Alignment(horizontal: .center, vertical: .bottom) theme.yOffset = -14 + + var pageIndex = 0 + let pageBinding = Binding(get: { + return pageIndex + }, set: { i in + pageIndex = i + }) - return HPageView(theme: theme) { + return HPageView(selectedPage: pageBinding, theme: theme) { v1 v2 } From c0b77d7fee8f0d9952646e84f30762bb215bcd46 Mon Sep 17 00:00:00 2001 From: Kacper Raczy Date: Thu, 18 Jun 2020 16:55:00 +0200 Subject: [PATCH 3/5] Customizable page switch threshold --- Sources/PageScrollState.swift | 10 ++++++---- Sources/PageView.swift | 23 ++++++++++++++++++++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Sources/PageScrollState.swift b/Sources/PageScrollState.swift index 9f10020..f286e60 100644 --- a/Sources/PageScrollState.swift +++ b/Sources/PageScrollState.swift @@ -17,12 +17,14 @@ class PageScrollState: ObservableObject { } // MARK: Properties - + + let switchThreshold: CGFloat @Binding var selectedPage: Int @Published var pageOffset: CGFloat = 0.0 @Published var isGestureActive: Bool = false - init(selectedPageBinding: Binding) { + init(switchThreshold: CGFloat, selectedPageBinding: Binding) { + self.switchThreshold = switchThreshold self._selectedPage = selectedPageBinding } @@ -58,9 +60,9 @@ class PageScrollState: ObservableObject { private func dragEnded(_ value: DragGesture.Value, viewCount: Int, dimension: CGFloat) { var newPage = selectedPage - if pageOffset > 0.5*dimension && selectedPage != 0 { + if pageOffset > switchThreshold*dimension && selectedPage != 0 { newPage -= 1 - } else if pageOffset < -0.5*dimension && selectedPage != viewCount - 1 { + } else if pageOffset < -switchThreshold*dimension && selectedPage != viewCount - 1 { newPage += 1 } diff --git a/Sources/PageView.swift b/Sources/PageView.swift index c197dc8..f949b27 100644 --- a/Sources/PageView.swift +++ b/Sources/PageView.swift @@ -17,11 +17,13 @@ public struct HPageView: View where Pages: View { public init( selectedPage: Binding, + pageSwitchThreshold: CGFloat = .defaultSwitchThreshold, theme: PageControlTheme = .default, @PageViewBuilder builder: () -> PageContainer ) { - let state = PageScrollState(selectedPageBinding: selectedPage) - self.state = state + // prevent values outside of 0...1 + let threshold = CGFloat(abs(pageSwitchThreshold) - floor(abs(pageSwitchThreshold))) + self.state = PageScrollState(switchThreshold: threshold, selectedPageBinding: selectedPage) self.theme = theme let pages = builder() self.pages = pages @@ -76,10 +78,13 @@ public struct VPageView: View where Pages: View { public init( selectedPage: Binding, + pageSwitchThreshold: CGFloat = .defaultSwitchThreshold, theme: PageControlTheme = .default, @PageViewBuilder builder: () -> PageContainer ) { - self.state = PageScrollState(selectedPageBinding: selectedPage) + // prevent values outside of 0...1 + let threshold = CGFloat(abs(pageSwitchThreshold) - floor(abs(pageSwitchThreshold))) + self.state = PageScrollState(switchThreshold: threshold, selectedPageBinding: selectedPage) self.theme = theme let pages = builder() self.pages = pages @@ -124,6 +129,18 @@ public struct VPageView: View where Pages: View { } } +extension CGFloat { + public static var defaultSwitchThreshold: CGFloat { + #if os(iOS) + return 0.3 + #elseif os(watchOS) + return 0.5 + #else + return 0.5 + #endif + } +} + #if DEBUG struct PageView_Previews: PreviewProvider { static var previews: some View { From 453bb466e539c20093600c1901cea62875f6ee31 Mon Sep 17 00:00:00 2001 From: Kacper Raczy Date: Thu, 18 Jun 2020 17:48:32 +0200 Subject: [PATCH 4/5] Update README.md --- PageView.xcodeproj/project.pbxproj | 4 +- README.md | 78 +++++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/PageView.xcodeproj/project.pbxproj b/PageView.xcodeproj/project.pbxproj index 446c66f..08d5e63 100644 --- a/PageView.xcodeproj/project.pbxproj +++ b/PageView.xcodeproj/project.pbxproj @@ -273,7 +273,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.3.1; + MARKETING_VERSION = 1.4.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited)"; @@ -303,7 +303,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 1.3.1; + MARKETING_VERSION = 1.4.0; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited)"; diff --git a/README.md b/README.md index 6a26ccb..c24436d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Package requires iOS 13, watchOS 6 and Xcode 11. For Swift Package Manager add the following package to your Package.swift: ```swift -.package(url: "https://github.com/fredyshox/PageView.git", .upToNextMajor(from: "1.3.1")), +.package(url: "https://github.com/fredyshox/PageView.git", .upToNextMajor(from: "1.4.0")), ``` ### Carthage @@ -26,7 +26,7 @@ For Swift Package Manager add the following package to your Package.swift: Carthage is also supported, add FormView by adding to Cartfile: ``` -github "fredyshox/PageView" ~> 1.3.2 +github "fredyshox/PageView" ~> 1.4.0 ``` ## Demo @@ -39,27 +39,51 @@ Demo app for both iOS and watchOS is provided in `Examples/` directory. import PageView ``` -PageView component is available as `HPageView` or `VPageView` depending on scroll direction (horizontal and vertical, respectively). -To add paged view with 3 pages use following code: +PageView component is available as `HPageView` or `VPageView` depending on scroll direction (horizontal and vertical, respectively). To add paged view with 3 pages use following code: + ```swift +@State var pageIndex = 0 + +... + // horizontal axis -HPageView { +HPageView(selectedPage: $pageIndex) { SomeCustomView() AnotherCustomView() AnotherCustomView() } // vertical axis -VPageView { +VPageView(selectedPage: $pageIndex) { SomeCustomView() AnotherCustomView() AnotherCustomView() } ``` -By default PageView fills all the available area, you can constrain it's size using `.frame(width:, height:)` View modifier. +By default PageView fills all the available area, you can constrain it's size using `.frame(width:, height:)` view modifier. + +### Selected page binding + +Displayed page can be programmatically controled using `Binding`. For example, you can change it, with animation effect using: + +```swift +withAnimation { + // page index is some State property, which binding was passes into PageView + self.pageIndex = 2 +} +``` + +### Page switch threshold + +You can also control minimum distance that needs to be scrolled to switch page, expressed in fraction of page dimension (width or height, depending on axis). This parameter is called `pageSwitchThreshold`, and must be in range from 0.0 to 1.0. + +For iOS the default value is set to `0.3`, while on watchOS `0.5`. + +### Theme + +Styling of page control component can be customized by passing `PageControlTheme`. Customizable properties: -You can customize the styling of page control component by passing `PageControlTheme`. Customizable properties: * `backgroundColor` * `dotActiveColor`: active page dot color * `dotInactiveColor`: inactive page dot color @@ -90,6 +114,44 @@ VPageView(theme: theme) { There is also a built-in `PageControlTheme.default` style, mimicking `UIPageControl` appearance. +## API + +```swift +// Horizontal page view +public struct HPageView: View where Pages: View { + public init( + selectedPage: Binding, + pageSwitchThreshold: CGFloat = .defaultSwitchThreshold, + theme: PageControlTheme = .default, + @PageViewBuilder builder: () -> PageContainer + ) +} + +// Vertical page view +public struct VPageView: View where Pages: View { + public init( + selectedPage: Binding, + pageSwitchThreshold: CGFloat = .defaultSwitchThreshold, + theme: PageControlTheme = .default, + @PageViewBuilder builder: () -> PageContainer + ) +} + +public struct PageControlTheme { + public var backgroundColor: Color + public var dotActiveColor: Color + public var dotInactiveColor: Color + public var dotSize: CGFloat + public var spacing: CGFloat + public var padding: CGFloat + public var xOffset: CGFloat + public var yOffset: CGFloat + public var alignment: Alignment? +} +``` + + + ## Screenshots ![iOS example](./Images/iOS-example.png) From 979d5137f2eb1446105d98ec8b5c37528e4107cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20R=C4=85czy?= Date: Thu, 18 Jun 2020 17:52:17 +0200 Subject: [PATCH 5/5] README.md formatting --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c24436d..44de612 100644 --- a/README.md +++ b/README.md @@ -119,22 +119,22 @@ There is also a built-in `PageControlTheme.default` style, mimicking `UIPageCont ```swift // Horizontal page view public struct HPageView: View where Pages: View { - public init( - selectedPage: Binding, - pageSwitchThreshold: CGFloat = .defaultSwitchThreshold, - theme: PageControlTheme = .default, - @PageViewBuilder builder: () -> PageContainer - ) + public init( + selectedPage: Binding, + pageSwitchThreshold: CGFloat = .defaultSwitchThreshold, + theme: PageControlTheme = .default, + @PageViewBuilder builder: () -> PageContainer + ) } // Vertical page view public struct VPageView: View where Pages: View { - public init( - selectedPage: Binding, - pageSwitchThreshold: CGFloat = .defaultSwitchThreshold, - theme: PageControlTheme = .default, - @PageViewBuilder builder: () -> PageContainer - ) + public init( + selectedPage: Binding, + pageSwitchThreshold: CGFloat = .defaultSwitchThreshold, + theme: PageControlTheme = .default, + @PageViewBuilder builder: () -> PageContainer + ) } public struct PageControlTheme {