-
-
Notifications
You must be signed in to change notification settings - Fork 171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pager's scroll blocks the ScrollView's scroll #18
Comments
HI @NeverwinterMoon , Thanks again for your feedback. I remember this issue, I finally chose to use When contained in a Gestures are something that I've been struggling a lot and I believe is something that needs a lot of improvement to resemble UIKit. Thanks again, will be in touch soon. |
Hi @NeverwinterMoon , I've given a look at this and doesn't seem to work. You suggestion var body: some View {
Button(action: {
self.isPresented.toggle()
}, label: {
Text("Present")
}).sheet(isPresented: $isPresented) {
self.presented
}
}
@State var data = Array(0..<10)
var presented: some View {
NavigationView {
GeometryReader { proxy in
ScrollView {
VStack {
HStack {
Pager(page: self.$page,
data: self.data,
id: \.self) {
self.pageView($0)
}.onPageChanged { page in
print(page)
}
.rotation3D()
.itemSpacing(10)
.itemAspectRatio(0.8, alignment: .end)
.padding(8)
.frame(width: min(proxy.size.height, proxy.size.width),
height: min(proxy.size.height, proxy.size.width))
.background(Color.gray.opacity(0.2))
}
Button(action: {
DispatchQueue.main.async {
self.data.remove(at: self.page)
self.page = min(self.page, self.data.count - 1)
print("\t\(self.data.count), \(self.page)")
}
}, label: {
Text("remove")
})
ForEach(Array(1...100), id: \.self) { _ in
Text("Item")
}
}
}
}.navigationBarTitle("SwiftUIPager", displayMode: .inline)
}
}
func pageView(_ page: Int) -> some View {
ZStack {
Rectangle()
.fill(Color.yellow)
Text("Page: \(page)")
.bold()
}
.cornerRadius(5)
.shadow(radius: 5)
} This is the worst case I can think of. As it is right now, Fernando |
Hi @NeverwinterMoon , Have you tried copy-pasting my code? Use a Fernando |
@fermoya Aha, I tried it out now with Also, imagine a situation with multiple
The page will not be vertically scrollable at all, and this is certainly not usable. |
I totally agree, it's very annoying. When you have a ScrollView inside another scrollview it works fine:
even if it is presented... By specifying the axis, it is able to just react to horizontal swipes, which would be ideal for Let's keep this open for future SwiftUI updates |
I'm having a very similar problem, I have a container that needs to react to vertical drags, and the pager should react to horizontal drags (basically, a clone of the Photos.app gallery view: swipe down to dismiss, swipe horizontally to page through). I'm very new to Swift and SwiftUI, but I was able to make it work by only updating the offset if the translation was within a certain sector (i.e. the angle was within the given bounds). This is the gesture handler for my container: .simultaneousGesture(DragGesture()
.updating($offset, body: { (value, state, transaction) in
let translation = value.translation
let angle = CGPointToDegree(value.startLocation - value.location)
print(angle)
let isInBounds = (20 < angle && angle < 160) || (-160 < angle && angle < -20)
if (isInBounds) {
state = translation
}
}) the edited .onChanged({ value in
let angle = CGPointToDegree(value.startLocation - value.location)
let isInBounds = (160 < angle && angle < 180) || (-180 < angle && angle < -160) || (-20 < angle && angle < 20)
if (isInBounds) {
withAnimation {
self.draggingStartTime = self.draggingStartTime ?? value.time
self.draggingOffset = value.translation.width
}
}
}) and some hacked-together helpers: func CGPointToDegree(_ point: CGPoint) -> Double {
// Provides a directional bearing from (0,0) to the given point.
// standard cartesian plain coords: X goes up, Y goes right
// result returns degrees, -180 to 180 ish: 0 degrees = up, -90 = left, 90 = right
let bearingRadians = atan2f(Float(point.y), Float(point.x))
let bearingDegrees = Double(bearingRadians) * (180 / .pi)
return bearingDegrees
}
func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
} This is specific to my application, but I also ended up changing item spacing when the user is dragging the Pager so that it doesn't get offsetted enough that normally hidden items come onto the display: .itemSpacing(self.offset == .zero ? 30 : 10000) The combination of angle checks and item spacing seems to work fairly well. Hope that helps. |
Hi @codetheweb, Thanks for your comment. There's an sample App in the repository which has a tab called "Presented": In UIKit, if you create a custom gesture which should only be triggered when scrolling horizontally, you make it fail when scrolling vertically. This way the gesture isn't recognized and some other View can try to recognize it. This isn't the case with SwiftUI, there's not such thing. The best workaround is suggested above, but again it introduces an issue with the "Presented" tab in the Sample project |
Adding minimum distance to DragGesture and modifier to set gesture as high priority
@NeverwinterMoon I'm gonna add that workaround you suggested and at the same time a modifier |
Version 1.5.0 released. Using suggested hack till SwiftUI offers new options. In case |
@fermoya I just tried this and using |
Hi @codetheweb ,
|
Sorry, I'm unclear on what you're saying. I should embed Pager inside a vertical ScrollView, then attach any gestures to that ScrollView? This modified example doesn't really work: //
// EmbeddedExampleView.swift
// SwiftUIPagerExample
//
// Created by Fernando Moya de Rivas on 02/03/2020.
// Copyright © 2020 Fernando Moya de Rivas. All rights reserved.
//
import SwiftUI
struct EmbeddedExampleView: View {
@State var page: Int = 0
var data = Array(0..<10)
@State var draggingOffset: CGSize = .zero
var colors: [Color] = [
.red, .blue, .black, .gray, .purple, .green, .orange, .pink, .yellow, .white
]
var body: some View {
NavigationView {
GeometryReader { proxy in
ScrollView {
VStack {
Pager(page: self.$page,
data: self.data,
id: \.self) { page in
self.pageView(page)
}
.rotation3D()
.itemSpacing(10)
.itemAspectRatio(0.8, alignment: .end)
.padding(8)
.frame(width: min(proxy.size.height, proxy.size.width),
height: min(proxy.size.height, proxy.size.width))
.background(Color.gray.opacity(0.2))
}
}
.offset(self.draggingOffset)
.gesture(DragGesture().onChanged { value in
withAnimation {
self.draggingOffset = value.translation
}
})
}.navigationBarTitle("SwiftUIPager", displayMode: .inline)
}
}
func pageView(_ page: Int) -> some View {
ZStack {
Rectangle()
.fill(Color.yellow)
Text("Page: \(page)")
.bold()
}
.cornerRadius(5)
.shadow(radius: 5)
}
} If further clarification is needed I can open a new issue. |
Hi @codetheweb , I don't understand the example. Why do you have a Maybe you're right and you should open a new issue and we discuss it there. |
I thought your comment meant that I should embed Pager inside ScrollView so that it only reacted to horizontal gestures. But I was able to get it working by using the By the way, you should set up GitHub Sponsors, Buy me a coffee, or similar. I'd be happy to send something your way and I'm sure others would too. 😄 |
thanks for the suggestion, @codetheweb . I was thinking of that, might do in the future 😄 Thanks a lot and apologies for the confusion |
I've seen there was a similar issue (#3) but then it was closed and an entirely different use case with ScrollView was discussed, so I am going to post this.
Basically, if a
Pager
view is placed inside aScrollView
with vertical scroll, the former blocks the parent's scroll, unless the drag gesture starts outside thePager
's view area.This is not an issue caused by
Pager
itself, but there might be solutions that would work if applied to the library. I haven't seen any perfect solutions yet though.There are some discussions here: https://stackoverflow.com/questions/57700396/adding-a-drag-gesture-in-swiftui-to-a-view-inside-a-scrollview-blocks-the-scroll. The trick with the button (https://stackoverflow.com/a/59961959/1719285) can be applied outside the library, but this then blocks the tap gestures inside each view (in my case, the remove button). The only solution that worked more or less OK (ish) was to have
DragGesture(minimumDistance: 30, coordinateSpace: .local)
for theswipeGesture
of the library.The text was updated successfully, but these errors were encountered: