Problem
How can I modify the scroll target of a scrollView? I am looking for kind of a replacement for the "classic" scrollView delegate method
override func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) 
...where we can modfify the targeted scrollView.contentOffset via targetContentOffset.pointee for instance to create a custom paging behaviour.
Or in other words: I do want to create a paging effect in a (horizontal) scrollView.
What I have tried ie. is something like this:
    ScrollView(.horizontal, showsIndicators: true, content: {             HStack(alignment: VerticalAlignment.top, spacing: 0, content: {                 card(title: "1")                 card(title: "2")                 card(title: "3")                 card(title: "4")             })      })     // 3.      .content.offset(x: self.dragState.isDragging == true ? self.originalOffset : self.modifiedOffset, y: 0)     // 4.      .animation(self.dragState.isDragging == true ? nil : Animation.spring())     // 5.     .gesture(horizontalDragGest)   Attempt
This is what I tried (besides a custom scrollView approach):
A scrollView has a content area larger then screen space to enable scrolling at all.
I created a DragGesture() to detect if there is a drag going on. In the .onChanged and .onEnded closures I modified my @State values to create a desired scrollTarget.
Conditionally fed in both the original unchanged and the new modified values into the .content.offset(x: y:) modifier - depending on the dragState as a replacement for missing scrollDelegate methods.
Added animation acting conditionally only when drag has ended.
Attached the gesture to the scrollView.
Long story short. It doesn't work. I hope I got across what my problem is.
Any solutions out there? Looking forward to any input. Thanks!
I have managed to achieve a paging behaviour with a @Binding index. The solution might look dirty, I'll explain my workarounds. 
The first thing I got wrong, was to get alignment to .leading instead of the default .center, otherwise the offset works unusual. Then I combined the binding and a local offset state. This kinda goes against the "Single source of truth" principle, but otherwise I had no idea how to handle external index changes and modify my offset. 
So, my code is the following
struct SwiftUIPagerView<Content: View & Identifiable>: View {      @Binding var index: Int     @State private var offset: CGFloat = 0     @State private var isGestureActive: Bool = false      // 1     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)                     }                 }             }             // 2             .content.offset(x: self.isGestureActive ? self.offset : -geometry.size.width * CGFloat(self.index))             // 3             .frame(width: geometry.size.width, height: nil, alignment: .leading)             .gesture(DragGesture().onChanged({ value in                 // 4                 self.isGestureActive = true                 // 5                 self.offset = value.translation.width + -geometry.size.width * CGFloat(self.index)             }).onEnded({ value in                 if -value.predictedEndTranslation.width > geometry.size.width / 2, self.index < self.pages.endIndex - 1 {                     self.index += 1                 }                 if value.predictedEndTranslation.width > geometry.size.width / 2, self.index > 0 {                     self.index -= 1                 }                 // 6                 withAnimation { self.offset = -geometry.size.width * CGFloat(self.index) }                 // 7                 DispatchQueue.main.async { self.isGestureActive = false }             }))         }     } }   .leading is mandatory if you don't want to translate all offsets to center.I have tested it in the following context
    struct WrapperView: View {          @State var index: Int = 0          var body: some View {             VStack {                 SwiftUIPagerView(index: $index, pages: (0..<4).map { index in TODOView(extraInfo: "\(index + 1)") })                  Picker(selection: self.$index.animation(.easeInOut), label: Text("")) {                     ForEach(0..<4) { page in Text("\(page + 1)").tag(page) }                 }                 .pickerStyle(SegmentedPickerStyle())                 .padding()             }         }     }   where TODOView is my custom view that indicates a view to implement.
I hope I get the question right, if not please specify which part should I focus on. Also I welcome any suggestions to remove the isGestureActive state. 
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With