Restrict angle of swipe to scroll UIScrollView - ios

I have a vertical scroll view that needs to be scrollable but I want to use the left and right swipe gestures for something else. I have the behavior for each swipe direction working, but I want to restrict the angle of a swipe that scrolls the UIScrollView, so that it has to be really, really vertical, and most left or right leaning swipes activate the other behavior and do not change the scroll position.
I know that I can get the swipe angle by overriding scrollViewDidScroll and comparing the previous and current contentOffset, but I cannot stop the scrollview from scrolling there.
How can I limit the angle of a swipe that scrolls a UIScrollView ?

I think scrollViewDidScroll is too late for what you want to achieve.
Maybe try setting the directionalLockEnabled property of the UIScrollView to YES.(not sure if this helps... but play with it)
And/Or implement the UIScrollViewDelegate method - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView and within this methods implementation grab a pointer to the panGestureRecognizer (readonly) property of the UIScrollView passed in.
From there call translationInView: on the UIPanGestureRecognizer, passing in the view in whose coordinate system the translation of the pan gesture should be computed, and with the CGPoint returned use trigonometry to calculate the angle and if it's not within predetermined limits call setTranslationInView: on the UIPanGestureRecognizer to restore.

Related

how to response to touchupinside event in a UIScrollView?

I have a UIScrollView in the centre of the whole view which is separated in to three parts and scroll horizontally.
Users can scroll the view freely and when the finger is up, i want to set one of the three parts to show based on the contentoffset of the UIScrollView.
how could i detect the touchupinside event in the UIScrollView? I tried add UITapGestureRecognizer and override touchesEnded but it does not work.
How about instead using the - scrollViewDidEndDecelerating:?
Keep in mind that just because the user has picked up their finger doesn't mean that the scrollview has stopped changing its content offset. By utilizing the - scrollViewDidEndDecelerating: delegate method, yo will be notified when the scroll view has come to a stop and at that point you can check the contentOffset and do what you need.
- scrollViewDidScroll: isn't a good match for what you want as it gets called even while the user's finger is still down. If that is important to you, then use - scrollViewDidEndDecelerating: as I mentioned above. If you'd rather know when the finger is lifted, and don't care that the scrollview is still in transit, then you can use - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate.

make superview respond to subView's UIPanGesture in some cases

I have a scrollView and it contains a subView. SubView has a panGesture added to it.
What I want:
I need pangesture to work on subview only if the panning is done vertically. If the panning happens horizontally, I want the scrollView to respond for the panning instead of subview. How can I acheive this
Note: Iam able to find programatically when the panning happens horizontally.
There are several ways to do this:
Depending on what your subview does, you maybe able to replace it with a scrollview and lock it to scroll only vertically. (while the super should scroll only horizontally).
You can try feeding the touch information from the subview to it's superview if the pan gesture is moving horizontally. You could do this using a delegate pattern or, if you know what you're superview is, just access superview in your subview. However, this creates a strong coupling between the two views and probably shouldn't be done. It would be fragile and many would consider it code smell.
(best option) - Subclass UIGestureRecognizer and create your own gesture recognizer that only recognizes vertical movement. Use that instead of the UIPanGestureRecognizer.
Option 3 is the best, but probably the most work. You'll want to read Apple's docs on subclassing UIGestureRecognizer: Apple Docs - UIGestureRecognizer.
I've also written my own drag gesture recognizer, FlxDragGestureRecognizer.m, FlxDragGestureRecognizer.h (similar to a pan gesture recognizer), which you can use if you'd like, or take a look at to get a good idea of how to subclass UIGestureRecognizer. You can use this class to recognize touches moving only in certain directions, otherwise it will fail (which will allow other gesture recognizer to recognizer, like the scrollview). It also has a lot of other customizations and information it gathers, like velocity.
Since you already know how to detect the panning, then in you will need to implement a delegate protocol in your superview, a delegate property in your subview and assign your superview as a delegate for your subview.
when detecting horizontal panning, call the delegate method, when detecting vertical panning, implement what you want in the subview.

Pass touches from a UIView to UIScrollView beneath it

I have a UIScrollView stretching over the entire screen, which the user is able to scroll vertically only. Right 'above' it is a UIView with a few buttons, which covers the bottom 120 px only. The user may tap the buttons to invoke their selectors. But I wish to be able to pass the panning movement to the scrollView, so that the user may scroll the scrollView if they pan with a velocity greater than a certain threshold, if the panning begins over the UIView.
How would I go about it?
Thanks
You can do 2 things.
Add a UITouchesMoved function to your uiView, calculate finger movement and move your scrollView accordingly by scrolling it to a CGPoint.
You can use UIPanGestureRecognizer.

Can't transition from UIScrollView to UIPanGesture in one movement

What I want to achieve: http://youtu.be/SUWxVtpc0JA
What I can achieve: http://youtu.be/b1xH6Xsvxcg
I’m trying to do the same vertical parallax scrolling effect that the Year Walk Companion app does when you change from text page to menu.
I’ve been able to do this effect on UIScrollViews that only move horizontally, by using a UIPanGestureRecognizer to move up and down, (imagine swiping between nested arrays). However when I have a UIScrollView with the vertical scrolling it breaks.
When I scroll down the UIScrollView it moves as you would expect, same if you move up. However the issues start when I want the UIPanGestureRecognizer to come in and move the UIScrollView itself vertically, not it’s content. To do this so far I have subclassed UIScrollView to make a test on the function gestureRecognizerShouldBegin :
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
UIPanGestureRecognizer* pan = (UIPanGestureRecognizer*)gestureRecognizer;
CGPoint panGestureTranslation = [pan translationInView:self];
if (self.contentOffset.y == 0 && panGestureTranslation.y > 0 ) {
return NO;
}
return YES;
}
It returns NO when my UIScrollView is at the top, and allows my other UIPanGesture for the parallax scrolling between pages to start again.
My problem is, when my UIScrollView is scrolling and reached the top, the function gestureRecognizerShouldBegin will not be called and force me to release the touch before allowing the UIPanGestureRecognizer to parallax scroll.
The question: How can I stop the handle of the touch by the UIScrollView and let the UIPanGestureRecognizer get the touch going up without release my finger from the screen? (like in the Year Walk Companion app).
gestureRecognizerShouldBegin isn't called because a new touch 'session' hasn't started. The existing one is still in progress and the gesture said it didn't want to start.
You could create your own gesture subclass which stays possible and only starts when the content offset < 0. Or you could use the scroll view delegate methods as your input and not use gestures at all.

UIPanGestureRecognizer with UIScrollView

I've been fighting with UIScrollView for a while now.
I have a scroll view which has multiple 'card' style views in it. I want the cards to move vertically, as in swiping up and down.
Imagine MobileSafari's tab view, but with swipe down to close tabs.
I can't figure out how to do this without it conflicting with the scroll view horizontal panning. I either get them both panning, or only one panning (vertical / horizontal).
What's the best practice to make this work perfectly, as in "if you're swiping vertically, stop horizontal swiping, and if you're swiping horizontally, stop vertical swiping".
Thank you!
Here's an illustration of what I want :
The scroll view has its own gesture recognizer: see its panGestureRecognizer property. If you add your own recognizer for detecting the vertical swipe, you can use requireGestureRecognizerToFail: or delegate methods to manage dependencies between the two recognizers.
I'm really confused by the intended behaviour of your app here. You are using swipe and panning interchangeably but they are different gesture recognizers.
Well one way to differentiate is to compare the x and y values of the translationInView: method of the gesture. If y > x, you have a vertical swipe/pan; x > y and you have a horizontal swipe/pan.
Make that gesture recognizer fail if the swipe/pan detected is not the type you are looking out for.

Resources