I want to make my scrollview less sensitive. This means that I want the user to have to move the finger a bit more before the view starts scrolling. Does anyone knows how to achieve this? I remember seeing a property that would let me set a required number of pixels before a scroll is detected. But it could just be my imagination. Perhaps what is required is to modify the underlying UIPanGestureRecognizer inside the scroll view?
The reason for this is that I am detecting a press on the view, so as long as the user has the finger there a function is constantly running. This function has to have the absolute priority, but the user might want to scroll the view instead, so I am canceling the function by detecting if the scrollview was scrolled. Everything works perfectly, except when the user is moving his hand/arm, his finger "might slip" a little bit, thus canceling the function since the scrollview starts scrolling.
Edit: (to address some of the confusion in the question)
How do I delay the scrolling in a scrollview by requiring the user to move the finger more before the scrollview starts scrolling?
I write an Answer so i can write code, but i am not entirely sure why you want the finger-offset. Lets assume you have an Area on your scrollview in which the user can longpress. During longpress you want to do some calculating and you do not want the scrollview to move. In my opinion there are two possibilties: Either you want the longpress to cancel if the finger moves a certain amount ("difficult to archive") or you want to supress the scrollview movement as long as the finger is pressed down, no matter what the movement is ("simple to archive").
Simple Solution:
- (void)yourUIBuilder {
...
UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressChanged:)];
[recognizer setDelegate:self];
[recognizer setMinimumPressDuration:0.3];
[yourScrollView addGestureRecognizer:recognizer];
}
- (void)longPressChanged:(UIGestureRecognizer *)recognizer {
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
// start your calculations
break;
case UIGestureRecognizerStateChanged:
break;
default:
// stop your calculations
break;
}
}
Related
I have a view in an iOS application (Obj-C) which has an image view in the centre, and immediately below that a slider.
The image view shows album artwork, and the slider can be used to adjust the now-playing track position.
There is also a pair of left and right Swipe Gesture Recognizers. These are used to skip to the next or previous tracks.
The problem is that the swipe gesture recognizers seem to over-ride the users moving the slider thumb.
In my gesture recognizer code I check that the point touched was inside the image view, but it still stops the slider from being moved. (The thumb moves, but jumps back to it's original position when you remove your finger).
This is the code I use to reject the gesture if it's not inside the image view.
- (IBAction)swipeLeftGestureAction:(UISwipeGestureRecognizer *)sender {
// Get the location of the gesture.
CGPoint tapPoint = [sender locationInView:_artworkImageView];
// Make sure tap was INSIDE the artwork image frame.
if( (tapPoint.x <0)
|| (tapPoint.y < 0 )
|| (tapPoint.x > _artworkImageView.frame.size.width)
|| (tapPoint.y>_artworkImageView.frame.size.height))
{
NSLog(#"Outside!");
return;
}
NSLog(#"Swipe LEFT");
[_mediaController skipNext];
}
So my question is, how do I limit the gesture to work ONLY when swiped across the image view?
Try to put the code that restricts the gesture's area in gestureRecognizer:shouldReceiveTouch: and return NO in case you don't want the gesture to receive this touch. It should prevent the gesture from over taking the slider interaction.
If you're only interested in swipes that are inside the image view, then you should add the swipe gesture recognizer to the image view instead of adding it to your entire view. Then you won't need any special logic.
There is a dedicated method for checking if a point is inside a view
BOOL isPointInsideView = [_artworkImageViewpointInside:tapPoint withEvent:nil];
But I think what is happening is that if you will look at the tapPoint is that it actually outside of your imageView
And if your slider is really close to the imageView so the slider captures the movement, what you should do is check on the slider if it is intended for the slider and propagate on to the imageView if needed
Or inherit the UISlider and reduce its response rect
Ok, here is the thing:
I need to have few animations happening on every screen change in my PageViewController.
So, when a user swipes, an image should fly in from the top left corner, from example.
I can make that animation to happen over time when user swipes and changes the page, but what I need is to that animation to be synchronised with the swipe movement itself.
So if a user presses the screen and starts swiping, the animation should follow user's finger and animate it's translation with the finger movement.
How can I achieve that?
I guess I need some sort of swipe gesture listener, but I failed to find any solution online. I guess I'm not using the right keywords.
You can use a UIPanGestureRecognizer to get the number of pixels swiped:
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panRecognizer_Panned:)];
[self.myViewToSwipe addGestureRecognizer:panRecognizer];
...
- (void) panRecognizer_Panned:(UIPanGestureRecognizer *)recognizer {
CGFloat pixelsMovedHoriz = [recognizer translationInView:self.vwRelativeTo].x;
}
I am interested in trying to modify the functionality that allows full-screen swiping from one view to another in order to create a "slider" that is the size of the entire page -- i.e. dragging/swiping/sliding anywhere on the page has an effect of some kind.
It doesn't need to be visible. For example, I might have a solid red screen that I can change the colour of by dragging to the right anywhere, having it gradually change to blue.
Is this possible?
Using a UIPanGestureRecognizer, you can track finger movement on the screen, then use translationInView to find out how much the user's finger moved by, and change the color somehow based on that number
UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[self.view addGestureRecognizer:pan];
And handle it:
-(void)handlePan:(UIPanGestureRecognizer*)sender
{
CGFloat xMovement = [sender translationInView:sender.view].x;
// Do something with the movement
// Then reset the translation
[sender setTranslation:CGPointZero inView:sender.view];
}
I have written a UIScrollView subclass that I am using to scroll a series of UITableViews. See the following diagram:
As you can see I have several vertically scrolling UITableViews, that are being scrolled horizontally inside a parent UIScrollView. This all works fine. However the application has a number of global gestures. For example, if I swipe in a given direction with 2 fingers, I do a UIView transition to another part of the app. but if I do the gesture on top of the scroll view and/or its child table views, they naturally scroll their content. This doesn't look good and causes some layout issues.
What I would like to figure out is how to disable all scrolling, on both the UIScrollView and its child UITableViews, when a user touches anywhere with two fingers, and only with two fingers. I've tried variations of overriding touchesBegan, touchesEnded, touchesShouldCancel etc... but I can't get it quite right. Any help is much appreciated.
Here is my gesture handling code:
UISwipeGestureRecognizer *twoFingerSwipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleTwoFingerSwipe:)];
[twoFingerSwipeUp setNumberOfTouchesRequired:2];
[twoFingerSwipeUp setDirection:UISwipeGestureRecognizerDirectionUp];
[twoFingerSwipeUp setDelegate:self];
// 'self' is the superview of the UIScrollView, which is a UIView.
[self addGestureRecognizer:twoFingerSwipeUp];
[twoFingerSwipeUp release];
// ... repeat the above code for up, down, left, right gestures ...
- (void)handleTwoFingerSwipe:(UISwipeGestureRecognizer*)swipeGesture {
switch ([swipeGesture direction]) {
case UISwipeGestureRecognizerDirectionUp:
[self changeToView:viewAbove];
break;
case UISwipeGestureRecognizerDirectionDown:
[self changeToView:viewBelow];
break;
case UISwipeGestureRecognizerDirectionRight:
[self changeToView:viewToTheRight];
break;
case UISwipeGestureRecognizerDirectionLeft:
[self changeToView:viewToTheLeft];
break;
}
}
Try setting panGestureRecognizer.maximumNumberOfTouches = 1 on all scroll and table views (iOS 5 only).
If you're using a swipe recogniser for the two-finger swipe, require the recognisers of the scroll view (including the table views — they're scroll view as well) to fail when the two-finger recogniser recognises its gesture.
[[scrollView panGestureRecognizer] requireGestureRecognizerToFail: twoFingerRecogniser];
Iterate the above code for every scroll view and table view.
(P.S.: "recogniser" is British English, not a spelling err.)
Hope that helps. :-)
Write this code:
scrollView.minimumZoomScale=1.0;scrollView.maximumZoomScale=1.0;
scrollView.delegate self];
And Here is scrollViewDelegate Method:-
-(UIView*)viewForZoomingInScrollView:(UIScrollView *)aScrollView{
return aScrollView;}
One thing that you should be doing is to check that the gesture has finished before acting upon it:
if (swipeGesture.state == UIGestureRecognizerStateEnded) {
// Do your think
}
I've known odd things to happen otherwise.
Just disable user interaction in the parent scroll view. You need a UIWindow subclass and override -sendEvent: method because this gets called BEFORE any gesture recognizer. There, if you detect two touches, send a notification. Let the scroll view listen to it and disable user interaction if it occurs. And if touches ended, let it re-enable user interaction.
I've got a little problem with the UIPanGestureRecognizer.
The Recognizer does not report the UIGestureRecognizerStateEnded
state if the user panned to the top left (means negative x and y
directions)
The state changes to UIGestureRecognizerStateEnded if any direction
is positive when the user lifts his finger, but it just ceases to report
actions if both directions are negative or zero.
This is bad because i hide some overlay views as long as the user
drags something around and those views do not return in failure case.
Of course I could setup a NSTimer to display the overlay after some
time automatically again but i can see no obvious error in my code and
I want it clean.
Is there something i missed? Is it an Apple Bug?
Initialization is like this:
pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panRecognized:)];
[self addGestureRecognizer:pan];
[pan release];
The handling function looks like this:
- (void)panRecognized:(UIPanGestureRecognizer *)gestureRecognizer {
switch ([gestureRecognizer state]) {
case UIGestureRecognizerStateBegan:
// fade some overlaying views out
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed:
// fade in the overlays
break;
default:
break;
}
// handle panning
}
The line
[self addGestureRecognizer:pan];
looks wrong to me.
It seems like you are creating the gesture recognizer from inside a UIView and not a UIViewController. So if the view is dealloc both it and the gesture recognizer will disappear.
Better to create the gesture recognizer from the UIViewController. Also the UIViewController needs to keep a strong point to the recognizer.