Skip to content

Instantly share code, notes, and snippets.

@ozalexo
Created October 24, 2019 04:42
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ozalexo/2d18b0d6daeb092b0abc1522a1d9c8d6 to your computer and use it in GitHub Desktop.
Save ozalexo/2d18b0d6daeb092b0abc1522a1d9c8d6 to your computer and use it in GitHub Desktop.
SwiftUI: ScrollView pager
//
// ContentView.swift
// PaginatedScrollView
//
// Created by Aleksey Ozerov on 24.10.2019.
// Copyright © 2019 Aleksey Ozerov. All rights reserved.
//
import SwiftUI
struct Page: View, Identifiable {
let id = UUID()
var body: some View {
VStack(spacing: 0) {
Text("Page")
}
.frame(width: 414, height: 300, alignment: .leading)
.background(getRandomColor())
}
}
struct SwiftUIPagerView<Content: View & Identifiable>: View {
@State private var index: Int = 0
@State private var offset: CGFloat = 0
var pages: [Content]
var body: some View {
GeometryReader { geometry in
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .center, spacing: 0) {
ForEach(self.pages) { page in
page
.frame(width: geometry.size.width, height: nil)
}
}
}
.content.offset(x: self.offset)
.frame(width: geometry.size.width, height: nil, alignment: .leading)
.gesture(DragGesture()
.onChanged({ value in
self.offset = value.translation.width - geometry.size.width * CGFloat(self.index)
})
.onEnded({ value in
if abs(value.predictedEndTranslation.width) >= geometry.size.width / 2 {
var nextIndex: Int = (value.predictedEndTranslation.width < 0) ? 1 : -1
nextIndex += self.index
self.index = nextIndex.keepIndexInRange(min: 0, max: self.pages.endIndex - 1)
}
withAnimation { self.offset = -geometry.size.width * CGFloat(self.index) }
})
)
}
}
}
struct ContentView: View {
var body: some View {
SwiftUIPagerView(
pages: (0..<4).map {
index in Page()
})
}
}
extension Int {
func keepIndexInRange(min: Int, max: Int) -> Int {
switch self {
case ..<min: return min
case max...: return max
default: return self
}
}
}
func getRandomColor() -> Color {
let r = Double.random(in: 0..<1)
let g = Double.random(in: 0..<1)
let b = Double.random(in: 0..<1)
return Color(red: r, green: g, blue: b, opacity: 1.0)
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
@DevAndArtist
Copy link

DevAndArtist commented Nov 11, 2019

Your solution has unnecessary code. You can drop the ScrollView and the call to content entirely. The ScrollView is discarded as soon as you call content and build the view hierarchy on that. ;)

@ozalexo
Copy link
Author

ozalexo commented Nov 12, 2019

It seems that you are right, Adrian. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment