Last active
April 4, 2025 02:21
-
-
Save EngOmarElsayed/49506d6b43b74ea5829ffc40680dd055 to your computer and use it in GitHub Desktop.
A custom Container I created to easily create Paging scroll view in swiftUI, article: https://thinkdiffrent.substack.com/p/creating-paging-scrollview-using
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Preview { | |
@Previewable @State var currentPage = 0 | |
HPagingScrollView(currentPage: $currentPage, spacing: 30, pageWidth: 200, pageHeight: 450) { | |
RoundedRectangle(cornerRadius: 20) | |
RoundedRectangle(cornerRadius: 20) | |
RoundedRectangle(cornerRadius: 20) | |
RoundedRectangle(cornerRadius: 20) | |
} | |
.isScrollDisabled(false) | |
} | |
// Mark: HPagingScrollView | |
struct HPagingScrollView<Content: View>: View { | |
@Binding var currentPage: Int | |
let spacing: CGFloat | |
let pageWidth: CGFloat | |
let pageHeight: CGFloat | |
@ViewBuilder let content: () -> Content | |
private let isScrollDisabled: Bool | |
init( | |
currentPage: Binding<Int>, | |
spacing: CGFloat, | |
pageWidth: CGFloat, | |
pageHeight: CGFloat, | |
@ViewBuilder content: @escaping () -> Content | |
) { | |
_currentPage = currentPage | |
self.spacing = spacing | |
self.pageWidth = pageWidth | |
self.pageHeight = pageHeight | |
self.content = content | |
self.isScrollDisabled = false | |
} | |
var body: some View { | |
_VariadicView.Tree( | |
_HPagingScrollViewRoot( | |
currentPage: $currentPage, | |
spacing: spacing, | |
pageWidth: pageWidth, | |
pageHeight: pageHeight, | |
isScrollDisabled: isScrollDisabled | |
) | |
) { | |
content() | |
} | |
} | |
} | |
//MARK: - Disable scroll modifier | |
extension HPagingScrollView { | |
private init( | |
currentPage: Binding<Int>, | |
spacing: CGFloat, | |
pageWidth: CGFloat, | |
pageHeight: CGFloat, | |
isScrollDisabled: Bool, | |
@ViewBuilder content: @escaping () -> Content | |
) { | |
_currentPage = currentPage | |
self.spacing = spacing | |
self.pageWidth = pageWidth | |
self.pageHeight = pageHeight | |
self.content = content | |
self.isScrollDisabled = isScrollDisabled | |
} | |
public func isScrollDisabled(_ isScrollDisabled: Bool) -> HPagingScrollView { | |
HPagingScrollView( | |
currentPage: $currentPage, | |
spacing: spacing, | |
pageWidth: pageWidth, | |
pageHeight: pageHeight, | |
isScrollDisabled: isScrollDisabled, | |
content: content | |
) | |
} | |
} | |
struct _HPagingScrollViewRoot: _VariadicView_MultiViewRoot { | |
@Binding var currentPage: Int | |
let spacing: CGFloat | |
let pageWidth: CGFloat | |
let pageHeight: CGFloat | |
let isScrollDisabled: Bool | |
private let screenWidth = UIScreen.main.bounds.width | |
private let screenHeight = UIScreen.main.bounds.height | |
func body(children: _VariadicView.Children) -> some View { | |
HStack(spacing: spacing) { | |
ForEach(children) { child in | |
child | |
.frame(width: pageWidth, height: pageHeight) | |
.id(child.id) | |
} | |
} | |
.padding(.horizontal, (screenWidth-pageWidth)/2) | |
.offset(x: CGFloat(currentPage) * -(pageWidth+spacing)) | |
.frame(width: screenWidth, alignment: .leading) | |
.gesture(scrollGesture(totalPages: children.count)) | |
} | |
private func scrollGesture(totalPages: Int) -> some Gesture { | |
DragGesture() | |
.onEnded({ value in | |
if value.translation.width < -50 && isScrollDisabled == false { incrementCurrentPage(totalPages: totalPages-1) } | |
if value.translation.width > 50 && isScrollDisabled == false { decrementCurrentPage() } | |
}) | |
} | |
private func incrementCurrentPage(totalPages: Int) { | |
withAnimation { | |
currentPage = currentPage == totalPages ? currentPage: currentPage+1 | |
} | |
} | |
private func decrementCurrentPage() { | |
withAnimation { | |
currentPage = currentPage == 0 ? 0: currentPage-1 | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Bash zphisher.sh