Disable UIScrollView inertia (scrollViewDidEndDecelerating) Swift - ios

Setup: I have a Scroll View that works great horizontally as well as a UISwipeGestureRecognizer that triggers a segue to another view when I swipe down.
Problem: If I am scrolling horizontally and I begin to swipe down (vertical scrolling disabled) WHILE the scrolling is decelerating, the swipe down action (segue) does not execute. It only works once the scrolling deceleration is complete.
Is there a way to disable the scroll deceleration (aka inertia) so that my swipe down gesture can be detected instantly? Perhaps there's a workaround to force UISwipeGestureRecognizer to be detected first?
Solutions in Swift are highly appreciated!

UIScrollView has a pinchGestureRecognizer and a panGestureRecognizer.If you have a UISwipeGestureRecognizer,the gesture will most likely be recognized as a UIPanGestureRecognizer.
You can add a dependence to solve the problem:
scrollView.panGestureRecognizer.requireGestureRecognizerToFail(swipeGesture)

Swift 3:
To disable decelerating scroll you can try:
func scrollViewWillEndDragging (scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
targetContentOffset.memory = scrollView.contentOffset
}
or you change this value:
scrollView.decelerationRate = UIScrollViewDecelerationRateNormal
// UIScrollViewDecelerationRateFast is the other param
About your gestureRecognizer interferences you can call this method to exclude touching from:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
// insert here views to exclude from gesture
if touch.view!.isDescendantOfView(self.excludeThisView) {
return false
}
return true
}

did you look here?
Deactivate UIScrollView decelerating
the answer
-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
[scrollView setContentOffset:scrollView.contentOffset animated:YES];
}
converted to swift is
func scrollViewWillBeginDecelerating(scrollView: UIScrollView) {
scrollView.setContentOffset(scrollView.contentOffset, animated: true)
}

Related

Is it possible to disable the swipe gesture and only make the pan gesture work in a scrollview?

I use a UIScrollView to show some images. I find both swipe gesture and pan gesture work. Is it possible to only make pan gesture work at a time?
I tried this code:
for gestureRecognizer in scrollView.gestureRecognizers ?? [] {
if gestureRecognizer is UISwipeGestureRecognizer) {
gestureRecognizer.isEnabled = false
}
}
It doesn't work. I print scrollView.gestureRecognizers!, get the gesture list:
UIScrollViewDelayedTouchesBeganGestureRecognizer
UIScrollViewPanGestureRecognizer
_UIDragAutoScrollGestureRecognizer
UIScrollViewPagingSwipeGestureRecognizer
I tried UIScrollViewPagingSwipeGestureRecognizer:
if gestureRecognizer is UIScrollViewPagingSwipeGestureRecognizer) {
gestureRecognizer.isEnabled = false
}
It says Use of undeclared type 'UIScrollViewPagingSwipeGestureRecognizer'.
If I understand you correctly... maybe you could use the following UIScrollViewDelegate method:
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
targetContentOffset.pointee = scrollView.contentOffset
}

How to override the handlePan selector in UICollectionView

In my tvOS app, I am trying to listen to changes in scrolling of my UICollectionView. After research, I found that the collection view natively receives a few gesture recognizers among them a UIPanGestureRecognizer with the selector handlePan:
<UIScrollViewPanGestureRecognizer: 0x101a4c1a0; state = Possible; delaysTouchesEnded = NO; view = <UICollectionView 0x1020c5d00>; target= <(action=handlePan:, target=<UICollectionView 0x1020c5d00>)>>
in the log, or in code:
myCollectionView.panGestureRecognizer
I was wondering if there's a way to add my controller as the target of the gesture recognizer, or maybe override the handlePan method.
I tried implementing the UIGestureRecognizerDelegate but it does not give me an access to the handlePan method.
Maybe I should just add a custom UIPanGestureRecognizer of my own on the collection view?
UICollectionView is a subclass of UIScrollView so you can detect scroll changes on collectionview by adding scrollview delegates.
Objective-C
// called on finger up if the user dragged. decelerate is true if it will continue moving afterwards
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
}
// called when scroll view grinds to a halt
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
}
Swift
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
}

Recognize swipe gesture in UIScrollView only when scrolling content reach the edge

I have UIScrollView with vertical scroll active. What I am trying to do is to add a swipe gesture with direction .down which will be recognized when the user cannot scroll the content anymore because it reaches the edge.
I was trying with require(toFail:) but it doesn't work properly.
let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture))
swipeDown.direction = UISwipeGestureRecognizerDirection.down
swipeDown.require(toFail: self.scrollView.panGestureRecognizer)
self.scrollView.addGestureRecognizer(swipeDown)
I have also added UIGestureRecognizerDelegate method to recognize simultaneously:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
How to give always priority to scrolling content inside scrollView and when it is not possible anymore to detect swipe?
OK, the way I have managed that issue was simply check if the contentOffset reaches point 0.0, and if yes then disable scrolling and activate additional gesture. For my case that was enough.
if scrollView.contentOffset.y == 0.0 {
print("content on top")
// use delegate method here to manage the gestures
}

How to measure velocity of Table View's scrolling?

I'd like to call or not call a method depending on how fast user has scrolled.
It has to be instantly measured as user's finger goes because the method needs to be either called or ignored the second it starts moving, not when it has stopped.
This works for me.
CGPoint scrollVelocity = [[self.tableView panGestureRecognizer] velocityInView:self.tableView];
NSLog(#"scroll velocity : %f",scrollVelocity.y);
This method exposes the velocity value. It is part of the scroll view delegate.
optional func scrollViewWillEndDragging(_ scrollView: UIScrollView,
withVelocity velocity: CGPoint,
targetContentOffset: UnsafeMutablePointer<CGPoint>){
}

UIPageViewController detecting pan gestures

Is there a way to determine the panning location of a UIPageViewController while sliding left/right? I have been trying to accomplish this but its not working. I have a UIPageViewController added as a subview and i can slide it horizontally left/right to switch between pages however i need to determine the x,y coordinates of where I am panning on the screen.
I figured out how to do this. Basically a UIPageViewController uses UIScrollViews as its subviews. I created a loop and set all the subviews that are UIScrollViews and assigned their delegates to my ViewController.
/**
* Set the UIScrollViews that are part of the UIPageViewController to delegate to this class,
* that way we can know when the user is panning left/right
*/
-(void)initializeScrollViewDelegates
{
UIScrollView *pageScrollView;
for (UIView* view in self.pageViewController.view.subviews){
if([view isKindOfClass:[UIScrollView class]])
{
pageScrollView = (UIScrollView *)view;
pageScrollView.delegate = self;
}
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSLog(#"Im scrolling, yay!");
}
My personal preference is not to rely too much on the internal structure of the PageViewController because it can be changed later which will break your code, unbeknownst to you.
My solution is to use a pan gesture recogniser. Inside viewDidLoad, add the following:
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handler))
gestureRecognizer.delegate = yourDelegate
view.addGestureRecognizer(gestureRecognizer)
Inside your yourDelegate's definition, you should implement the following method to allow your gesture recogniser to process the touches
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Now, you should be able to access the X/Y location of the user's touches:
func handler(_ sender: UIPanGestureRecognizer) {
let totalTranslation = sender.translation(in: view)
//...
}

Resources