I need to animate view translation from A to B on the screen.
However, I want that translation to occur when a user swipes his thumb on the screen.
I also want translation to depend on the thumb swipe in a way that they follow one another.
I assume I will need some sort of listener, which will follow my thumb motion on the screen and I would somehow tell my view to move on the screen left or right, depending on the direction of the swipe.
How can I achieve that?
I would use the UIPanGestureRecognizer, because it has more control, for example if you move slow, it can still pickup where your movement is, then maybe you can position your translation accordingly.
You can do something like:
var panRecongniser = UIPanGestureRecognizer(target: self, action: Selector("didPanRecongnised:"))
in your viewDidLoad, and then:
func didPanRecongnised(recongniser: UIPanGestureRecognizer){
if(recongniser.state == UIGestureRecognizerState.Changed || recongniser.state == UIGestureRecognizerState.Began){
self.didPanMove(recongniser)
}else if(recongniser.state == UIGestureRecognizerState.Ended || recongniser.state == UIGestureRecognizerState.Cancelled){
self.didPanEnd(recongniser)
}
}
and in your didPanMove:
func didPanMove(recongniser: UIPanGestureRecognizer){
if((self.view.superview) != nil){
if(recongniser.state == UIGestureRecognizerState.Began){
// save the original position
self._origPoint = self.view.frame.origin
}
// this is the translation of the last segment of the "move"
let trans = recongniser.translationInView(self.view.superview!)
// this is the velocity of the last segment of the "move"
let velocity = recongniser.velocityInView(self.view.superview!)
}
}
Use the translation values to workout which direction the user is swiping. Don't forget to add the "trans" value as the accumulated translation each time "didPanMove" is called.
You can now use those value to do a number of things. Like having the appearing view to follow the progress of your finger. Or when the velocity reaches certain speed, having the view "snap" to the required position, like with a swipe. And maybe if the user don't swipe fast enough or far enough, having the view follow the finger a bit, then "snap" back to the hiding position when the gesture ends.
To do the animation, if you're doing a custom sliding out view, you can probably use UIView.animateWithDuration to animate your view sliding out.
I hope it helps you...
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(tappedRightButton:)];
[swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.view addGestureRecognizer:swipeLeft];
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(tappedLeftButton:)];
[swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
[self.view addGestureRecognizer:swipeRight];
Related
If you start pressing the screen, but move the finger because minimumPressDuration ellapses, the gesture gets cancelled and your movement gets forwared to the view. If minimumPressDuration is reached, it doesn't matter how much you move the finger. I want to avoid this and always cancel my gesture if the finger movement is bigger than allowableMovement.
I've seen this thread, but that solution isn't working for me.
I've tried subclassing UILongPressureGestureRecognizer, and set the state to failed or cancelled when my requirement is met, but doesn't seem to work, I guess cancelling it isn't enough and have to forward the events myself? How should I do this? My intention is to use it together with MKMapView. I'm getting really frustrated with this, I've tried it for two days.
Try this one :
UILongPressGestureRecognizer *longpress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(LongPress_action:)];
longpress.minimumPressDuration = 2.0; //seconds
longpress.delegate = self;
[yourview addGestureRecognizer:longpress]; // where you want to add
Call this method :
- (void)LongPress_action:(UILongPressGestureRecognizer*)gesture
{
if ( gesture.state == UIGestureRecognizerStateEnded )
{
NSLog(#"Long Press");
}
}
I'm having an issue with swipe gesture recognizer - it is sometimes slow and requires some distance to detect. For example sometime when you move your finger just a little it won't detect.
I'm making a twitch game and handle my input with gestures in all four directions and this behavior leads to all sorts of confusion and irritates players.
So I figured I need to implement my own swipe recognizer. Or maybe there is a library done by someone else?
My thinking so far is to store a location of touch in touchesBegan: method and then check for new locations in touchesMoved: or touchesEnded: methods. Then I will compare the distance and direction and fire correct methods.
Is this a correct way to do this or am I missing something?
It is a correct way but far from easy actually. I created a class that does something like that for me (for throwing a view actually).
What I do in this class is generate a point buffer and a date-time buffer which I can then use to compute things like speed, gesture length... On the outside it looks like this:
#property NSInteger maximumBufferSize;
#property (readonly) CGFloat traceLength;
#property (readonly) CGPoint traceSpeed;
- (void)begin:(CGPoint)point;
- (void)push:(CGPoint)point;
- (void)end:(CGPoint)point;
Pretty convenient to use. In your case begin, push and end are touches began, touches moved and touches ended (or canceled). Maximum buffer size is the number of points it will store (N last points received by touches that is).
You must understand you will probably need a bit more then that if you want to ignore none swipe gestures (user draws a circle with his finger for instance). To do such things you might want to compute an average way of the trace and then compare each sub-trace (point[i+1] - point[i]) so that dot(trace.averageWay, sub-trace.averageWay) = 1+-trashold.
Edit: added source link
Source link
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
// Setting the swipe direction.
[swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
// Adding the swipe gesture on image view
[self.imageView addGestureRecognizer:swipeLeft];
[self.imageView addGestureRecognizer:swipeRight];
- (void)handleSwipe:(UISwipeGestureRecognizer *)swipe {
if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
}
if (swipe.direction == UISwipeGestureRecognizerDirectionRight) {
}
}
You can read this tutorial
[http://www.raywenderlich.com/44270/sprite-kit-tutorial-how-to-drag-and-drop-sprites]
I make sure that you will find the solutions for your problems.
Throughout iOS 7 there's many situations where users can slide their finger in from the left or right edge of the screen in order to perform an action, such as popping a view controller or showing a sidebar.
Is there a built in way to do this that I've completely overlooked somehow (yes, I've searched extensively)? Or is the only way to check the frame position of where the pan started?
This is because I want to perform distinct actions if the user pulls from the edge, or say the middle.
You have UIScreenEdgePanGestureRecognizer, which is added to iOS7 to detect, well, panning from the edges of the screen. For panning from the middle, a normal pan gesture recognizer will suffice, where you can check if the pan gesture originated close enough to the middle.
Use the UIScreenEdgePanGestureRecognizer, but check that it's available first (since it's iOS 7+):
if (NSClassFromString(#"UIScreenEdgePanGestureRecognizer")) {
UIScreenEdgePanGestureRecognizer *panRecognizer =
[[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self
action:#selector(handleScreenEdgePanGesture:)];
panRecognizer.edges = UIRectEdgeLeft;
}
Set up gestures
UIPanGestureRecognizer* panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanning:)];
[self.view addGestureRecognizer:panGesture];
Handling gesture states
- (void)handlePanning:(UIPanGestureRecognizer *)gestureRecognizer
{
switch ([gestureRecognizer state])
{
case UIGestureRecognizerStateBegan:
[self startDragging:gestureRecognizer];
break;
//u won't need following cases
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateFailed:
[self stopDragging:gestureRecognizer];
break;
default:
break;
}
}
Recognizing start point of drag
- (void)startDragging:(UIPanGestureRecognizer *)gestureRecognizer
{
CGPoint pointInSrc = [gestureRecognizer locationInView:yourVIEW];
}
The pan gesture recognizer is a predefined recognizer, about all you can do with it is to determine how fast the user moved their finger. If you want to tell if where the movement started, you'll have to code your own recognizer. It's not that difficult, you'll be notified when the touch started and where it ended.
is it possible to let a specific gesture fail so the next possible gesture is recognized?
to be more specific, look at the sample snippet:
UISwipeGestureRecognizer *swipeLeft = [initialize UISwipeGestureRecognizer... #selector(handleSwipe:)]
swipeLeft = UISwipeGestureRecognizerDirectionLeft;
swipeLeft.delegate = self;
UIPanGestureRecognizer *pan = [initialize UIPanGestureRecognizer... #selector(handlePan:)]
pan.delegate = self;
[pan requireGestureRecognizerToFail:swipeLeft];
the above code states that if swipe left is not recognized by the device, pan gesture handler will be used.
so my question: is it possible to let swipeLeft intentionally fail (after being recognized as swipe left touch by the device) based on some criteria that is checked on handleSwipe, and let the pan gesture handle the touch input instead?
thanks.
Check out the UIGestureRecognizerDelegate protocol here:
https://developer.apple.com/library/ios/documentation/uikit/reference/UIGestureRecognizerDelegate_Protocol/Reference/Reference.html
Specifically, the
gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
method might be useful. If you simply return YES from this method, both gestures can be recognized at the same time, so you can respond properly to both.
Assuming you have some other handler implemented for the pan gesture, can't you just do:
-(void)handleSwipe:(id)sender {
if //criteria is met to ignore left swipe
{
[self handlePan:self];
}
}
-(void)handlePan:(id)sender {
// handle pan gesture here
}
Right now my UIPanGestureRecognizer recognizes every single pan, which is great and necessary, but as I'm using it as a sliding gesture to increase and decrease a variable's value, within the method I only want to act every so often. If I increment by even 1 every time it's detected the value goes up far too fast.
Is there a way to do something like, every 10 pixels of panning do this, or something similar?
You're looking for translationInView:, which tells you how far the pan has progressed and can be tested against your minimum distance. This solution doesn't cover the case where you go back and forth in one direction in an amount equal to the minimum distance, but if that's important for your scenario it's not too hard to add.
#define kMinimumPanDistance 100.0f
UIPanGestureRecognizer *recognizer;
CGPoint lastRecognizedInterval;
- (void)viewDidLoad {
[super viewDidLoad];
recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(didRecognizePan:)];
[self.view addGestureRecognizer:recognizer];
}
- (void)didRecognizePan:(UIPanGestureRecognizer*)sender {
CGPoint thisInterval = [recognizer translationInView:self.view];
if (abs(lastRecognizedInterval.x - thisInterval.x) > kMinimumPanDistance ||
abs(lastRecognizedInterval.y - thisInterval.y) > kMinimumPanDistance) {
lastRecognizedInterval = thisInterval;
// you would add your method call here
}
}