import SwiftUI
struct PagingCollectionUI<T: Hashable, Content: View>: View {
@State private var currentIndex: Int = 0
@State private var offset: CGFloat = 0
@State private var startOffset: CGFloat = 0
@Binding var items: [T]
var height: CGFloat = 100
var padding: CGFloat = 20.0
var spacing: CGFloat = 10.0
let contentView: (T) -> Content
var body: some View {
VStack {
GeometryReader { geometry in
let cellWidth = geometry.size.width - (padding * 2)
ScrollView(.horizontal, showsIndicators: true) {
LazyHStack(spacing: spacing) {
ForEach(items, id: \.self) { index in
contentView(index)
.frame(width: cellWidth, height: height)
}
}
.padding(.horizontal, padding)
}
.content.offset(x: offset)
.gesture(
DragGesture()
.onChanged { value in
offset = startOffset + value.translation.width
}
.onEnded { value in
var newIndex = currentIndex
let moveWidth = value.predictedEndTranslation.width
if newIndex < items.count - 1, moveWidth < -(cellWidth / 2) {
print("right")
newIndex += 1
} else if newIndex > 0, moveWidth > cellWidth / 2 {
print("left")
newIndex -= 1
}
let targetOffset = CGFloat(newIndex) * (cellWidth + spacing)
withAnimation(.easeOut) {
currentIndex = newIndex
offset = -targetOffset
startOffset = offset
}
}
)
}
}
.clipped()
}
}
extension Array {
func randomElements(count: Int) -> [Element] {
var elements = self
var result: [Element] = []
for _ in 0..<count {
guard !elements.isEmpty else { break }
let randomIndex = Int.random(in: 0..<elements.count)
result.append(elements[randomIndex])
elements.remove(at: randomIndex)
}
return result
}
}
struct PagingCollectionUI_Container: View {
@State var items1 = Array(0...10)
@State var items2 = Array(0...10)
@State var lastValue = 0
var body: some View {
ScrollView {
VStack(alignment: .center) {
PagingCollectionUI(items: $items1) { index in
Text("Item \(index)")
.frame(maxWidth: .infinity)
.frame(maxHeight: .infinity)
.background(.blue)
}
.frame(height: 200)
.background(.gray)
PagingCollectionUI(items: $items2, height: 300, padding: 50, spacing: 40) { index in
Text("Item \(index)")
.frame(maxWidth: .infinity)
.frame(maxHeight: .infinity)
.background(.white)
}
.frame(height: 400)
.background(.gray)
HStack {
PagingCollectionUI(items: $items2, height: 150) { index in
VStack {
Text("Item \(index)")
.frame(maxWidth: .infinity)
.frame(maxHeight: .infinity)
.background(.red)
}
.cornerRadius(10)
}
.frame(width: 200, height: 200)
.background(.gray)
PagingCollectionUI(items: $items2, height: 150) { index in
VStack {
Text("Item \(index)")
.frame(maxWidth: .infinity)
.frame(maxHeight: .infinity)
.background(.red)
}
.cornerRadius(10)
}
.frame(width: 200, height: 200)
.background(.gray)
}
}
}
}
}
struct PagingCollectionUI_Previews: PreviewProvider {
static var previews: some View {
PagingCollectionUI_Container()
}
}