I'm trying to make a vertical line from the top of my main view to the bottom, and it will trigger an action when someone swipes and crosses this line from either direction. I've already tried creating a tall thin label and putting a swipe recognizer inside of this, but it does not work the way I want. It only detects a swipe when you start inside of the label and swipe but not if you start outside the label and cross through it.
Here is my code so far,
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(doSomething)];
swipe.direction = UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight;
swipe.numberOfTouchesRequired = 1;
[self.myLabel setUserInteractionEnabled:YES];
[self.myLabel addGestureRecognizer:swipe];
The easiest way is to use the touchesBegan: and touchesMoved: method on a UIView to decide if it's crossed the midway line. You would then just add a view (your line) on top of this view, and you may want to disable user interaction on your "line" view. BTW, a quick dirty way to create a line is to create a view that's 2px wide (or whatever width you want) and set it's background color to your "line" color...
You need to decide in touchesBegan: which side they started on, and then do something like this in touchesMoved::
- (void) touchesMoved: (NSSet *)touches withEvent:(UIEvent *)event {
CGPoint tappedPt = [[touches anyObject] locationInView: self];
if (tappedPt.x > self.frame.width/2 && self.userStartedOnLeftSide)
// User crossed the line...
else if (tappedPt.x < self.frame.width/2 && !self.userStartedOnLeftSide)
// User crossed the line...
else
// You probably don't need this else, the user DID NOT cross the line...
}
Similarly, you could do this in a UIViewController with a swipe gesture recognizer something like this:
- (void)swipe:(UISwipeGestureRecognizer *)recognizer{
CGPoint point = [recognizer locationInView:[recognizer view]];
if (recognizer.state == UIGestureRecognizerStateBegan)
// The user began their swipe at point
else if (recognizer.state == UIGestureRecognizerStateEnded)
// The user ended their swipe at point
// Add some logic to decide if they crossed the line...
}
If you're just trying to detect left and right swipes, you can put it on your main view like so:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UISwipeGestureRecognizer *swipe;
swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
swipe.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipe];
swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
swipe.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swipe];
}
- (void)handleSwipe:(UISwipeGestureRecognizer *)gesture
{
NSLog(#"%d", [gesture numberOfTouches]);
if (gesture.direction == UISwipeGestureRecognizerDirectionLeft)
{
NSLog(#"LEFT");
}
if (gesture.direction == UISwipeGestureRecognizerDirectionRight)
{
NSLog(#"RIGHT");
}
}
If you wanted to really test to see if you crossed the midpoint, you could use aUIPanGestureRecognizer:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)]];
}
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
static CGPoint startLocation;
static BOOL startAtLeft;
CGPoint location = [gesture locationInView:gesture.view];
if (gesture.state == UIGestureRecognizerStateBegan)
{
startLocation = location;
startAtLeft = (location.x < (gesture.view.frame.size.width / 2.0));
}
else if (gesture.state == UIGestureRecognizerStateEnded)
{
BOOL nowAtLeft = (location.x < (gesture.view.frame.size.width / 2.0));
if (startAtLeft != nowAtLeft)
{
if (startAtLeft)
NSLog(#"swiped across midpoint, left to right");
else
NSLog(#"swiped across midpoint, right to left");
}
}
}
With the pan gesture, you can customize this to your heart's content (e.g. recognize gesture as soon as the user's finger crosses rather than waiting for them to end the gesture, etc.).
Related
Hi I have some buttons that trigger action methods.
In addition, I would like the user to be able to swipe left or right over the area with the buttons to show some different buttons
My idea was to add a UIView with a clear background color on top of the buttons so that the user could still see the buttons but could also swipe to the right or left.
However, when the view is over the buttons the buttons no longer work and when it is under the buttons, the swipe is not detected.
In addition to changing the view color to clear, I also tried adjusting the alpha however this seems to work the same way. When alpha of the UIView is zero, swipes are no longer detected and when alpha is greater than zero, the buttons no longer work.
Can anyone suggest a way to do this?
Thanks in advance for any suggestions.
Code I am using for buttons and to detect swipes seems standard. Trying to figure out a way to expose view and buttons at the same time.
Action methods to which buttons are wired:
//.h file
- (IBAction)showIncoming:(id)sender;
- (IBAction)showOutcoming:(id)sender;
/.m file
- (IBAction)showIncoming:(id)sender {
_showIncoming=YES;
_fetchedResultsController=nil;
[self.tableView reloadData];
[self updateInterface];
}
- (IBAction)showOutgoing:(id)sender {
_showIncoming=NO;
_fetchedResultsController=nil;
[self.tableView reloadData];
[self updateInterface];
}
Swipes:
//in viewWillAppear
UISwipeGestureRecognizer *rightRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(rightSwipeHandle:)];
rightRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[rightRecognizer setNumberOfTouchesRequired:1];
rightRecognizer.delegate = self;
[_topView addGestureRecognizer:rightRecognizer];
[_topView setUserInteractionEnabled:YES];
// [rightRecognizer release];
//........towards left Gesture recogniser for swiping.....//
UISwipeGestureRecognizer *leftRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(leftSwipeHandle:)];
leftRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[leftRecognizer setNumberOfTouchesRequired:1];
leftRecognizer.delegate = self;
[_topView addGestureRecognizer:leftRecognizer];
[_topView setUserInteractionEnabled:YES];
}
- (void)rightSwipeHandle:(UISwipeGestureRecognizer*)gestureRecognizer
{
//Do moving
NSLog(#"Right Swipe performed");
_showOld=YES;
[self updateInterface];
}
- (void)leftSwipeHandle:(UISwipeGestureRecognizer*)gestureRecognizer
{
// do moving
NSLog(#"Left Swipe performed");
_showOld=NO;
[self updateInterface];
}
The approach I suggest is listening to a user moving a finger events and firing specified method that would check if user's finger is on your button's view. Sample code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchedViewWithTouches:touches andEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchedViewWithTouches:touches andEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchedViewWithTouches:touches andEvent:event];
}
- (void)didTouchedButton:(NSSet *)touches andEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
UIView *touchedView;
for (UIView *view in self.subviews) {
if(CGRectContainsPoint(view.frame, touchLocation)) {
touchedView = view;
break;
}
}
if (view == self.showIncomingButton) {
[self showIncoming:self.showIncomingButton];
} else if (view == self.showOutcomingButton) {
[self howOutcoming:self.showOutcomingButton];
}
}
Note that you will have to add two outlets for your buttons (if you don't have any). Also, you can get rid off (id)sender in your IBAction calls.
I have leftMenu appears when swipe to right, and I want my tab changes when swipe to left whole page.
Their UIPanGestureRecognizer intercepts, I have tried disabling bouncing of UIScrollView of UIPageViewController, tried requireGestureRecognizerToFail:
But I could not make it both of them works. What I want is when it is swiped to to right side menu appears. When it is swiped to left if sidemenu is open it will close, if not, page will change.
My sample code is as follows;
for(UIScrollView *view in self.pageViewController.view.subviews)
{
if ([view isKindOfClass:[UIScrollView class]])
{
UIScrollView *scrollView = (UIScrollView *)view;
AppDelegate *appDel = [UIApplication sharedApplication].delegate;
UIPanGestureRecognizer* panGestureRecognizer = scrollView.panGestureRecognizer;
[panGestureRecognizer addTarget:self action:#selector(move:)];
[panGestureRecognizer requireGestureRecognizerToFail:appDel.slidingViewController.panGesture];
}
}
Thanks in advance
Something like the following should work (warning: untested!)
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panRecognized:)];
panRecognizer.delegate = self;
for (UIGestureRecognizer *recognizer in self.pageViewController.gestureRecognizers)
[recognizer requireGestureRecognizerToFail:panRecognizer];
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)recogniser
{
if (self.leftMenuShowing)
return YES;
else
{
UIPanGestureRecognizer *panRecognizer = (id)recognizer;
CGPoint translation = [panRecognizer translationInView:self.view];
return (translation.x > 0);
}
}
- (void)panRecognized:(UIPanGestureRecognizer *panRecognizer)
{
if (state == UIGestureRecognizerStateBegan || state == UIGestureRecognizerStateChanged)
{
// if pan is to left, move leftMenu offscreen
// if pan is to right, move leftMenu onscreen
}
else
{
// if leftMenu is mostly onscreen, animate completely onscreen
// if leftMenu is mostly offscreen, animate completely offscreen
}
}
I am trying to mix an UISwipeGestureRecognizer with an UITableView.
What I would like to do is, while I am doing the swipe gesture, move at the same time the UITableView outside the window and refresh the table data.
I am going to show you with images...
This is my view:
My View
And I would like to get something like this:
Desired View
I am able to move the table when the swipe gesture is ended, but not while I am doing the gesture, and that is what I want.
This is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
swipeToRight = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(changeToGroup:)];
[swipeToRight setDelegate:self];
[swipeToRight setDirection:UISwipeGestureRecognizerDirectionRight];
[[self table]addGestureRecognizer:swipeToRight];
swipeToLeft = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(changeToContact:)];
[swipeToLeft setDelegate:self];
[swipeToLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
[[self table]addGestureRecognizer:swipeToLeft];
}
- (void)changeToGroup:(UISwipeGestureRecognizer *)swipeGesture
{
NSLog(#"Right to group");
[self updateTableData]; //Here I move the table and update data.
[segmentedControl setSelectedSegmentIndex:0];
}
- (void)changeToContact:(UISwipeGestureRecognizer *)swipeGesture
{
NSLog(#"Left to contact");
[self updateTableData]; //Here I move the table and update data.
[segmentedControl setSelectedSegmentIndex:1];
}
I thought that I could do it with UIGestureRecognizerStateBegan, adding the animations inside that event, but I can't receive it...
Could anyone help me?
Thanks a lot!!.
Try this,
use UIPanGestureRecognizer
UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(handlePan:)];
[recognizer setMaximumNumberOfTouches:1];
[recognizer setDelegate:self];
[[self table]addGestureRecognizer:recognizer];
and in
- (void) handlePan:(UIPanGestureRecognizer *)recognizer {
UIView *view = self.tableView;
if(recognizer.state == UIGestureRecognizerStateBegan) {
// remember where the pan started
//panGestureOrigin is a CGPoint variable
panGestureOrigin = view.frame.origin;
//optional to keep an enum to keep direction
//self.panDirection = MenuPanDirectionNone;
}
CGPoint translatedPoint = [recognizer translationInView:view];
if(translatedPoint.x > 20) {
//self.panDirection = MenuPanDirectionRight;
[self handleRightPan:recognizer];
} else if(translatedPoint.x < 0) {
//self.panDirection = MenuPanDirectionLeft;
[self handleLeftPan:recognizer];
}
}
- (void) handleRightPan:(UIPanGestureRecognizer *)recognizer {
//animate here
if(recognizer.state == UIGestureRecognizerStateEnded) {
NSLog(#"Right to group");
[self updateTableData]; //Here I move the table and update data.
[segmentedControl setSelectedSegmentIndex:0];
}
}
- (void) handleLeftPan:(UIPanGestureRecognizer *)recognizer {
// animate here
if(recognizer.state == UIGestureRecognizerStateEnded) {
NSLog(#"Left to contact");
[self updateTableData]; //Here I move the table and update data.
[segmentedControl setSelectedSegmentIndex:1];
}
}
I have a custom implementation of UITableViewCell.
The UITableViewCell can be swiped to the left or the right.
I use a UIPanGestureRecognizer for the same.
UIGestureRecognizer* recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
recognizer.delegate = self;
[self addGestureRecognizer:recognizer];
}
#pragma mark - horizontal pan gesture methods
-(BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
CGPoint translation = [gestureRecognizer translationInView:[self superview]];
// Check for horizontal gesture
if (fabsf(translation.x) > fabsf(translation.y)) {
return YES;
}
return NO;
}
-(void)handlePan:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
// if the gesture has just started, record the current centre location
// SOME CODE
}
if (recognizer.state == UIGestureRecognizerStateChanged) {
// translate the center
//SOME CODE
}
if (recognizer.state == UIGestureRecognizerStateEnded) {
// the frame this cell would have had before being dragged
//SOME CODE
}
}
Now I want to be able to support two finger swipe on on the entire screen such that even if a two finger swipe is done on a UITableViewCell, the above code is not triggered.
How can I achieve this ?
If you set the maximumNumberOfTouches on your gesture recognizer to 1, it will no longer accept multi-finger swipes:
UIGestureRecognizer* recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
recognizer.maximumNumberOfTouches = 1;
I have a couple of UIScrollViews in my view controller. I want to overlay a view that captures a 2 finger swipe via UIPanGestureRecognizer which will not record the UIScrollView swipe gestures.
When I put a transparent view over my content with a 2 finger pan gesture, my taps and 1 finger swipes are not detected. I tried overwriting the pointInside: method to return NO
but then it doesn't record my 2 finger swipe.
The effect is similar to the 4 finger swipe to change apps.
You don't need an overlay view.
First implement UIPanGestureRecognizer that will handle 2 finger pan and assign it to your view that contains UIScrollViews
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]
initWithTarget:self
action:#selector(handlePan:)];
panGestureRecognizer.delegate = self;
panGestureRecognizer.minimumNumberOfTouches = 2;
panGestureRecognizer.maximumNumberOfTouches = 2;
[self.view addGestureRecognizer:panGestureRecognizer];
Use UIGestureRecognizerDelegate to handle 2 finger pan with UIScrollView pan gesture
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
And finally you are able to handle 2 fingers pan
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer
{
NSLog(#"pan");
}
If you want to stop scrolling UIScrollView when two finger pan is detected you can disable and enable UIScrollView pan recognizers
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer
{
if(gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
_scrollView.panGestureRecognizer.enabled = NO;
}
if(gestureRecognizer.state == UIGestureRecognizerStateEnded)
{
_scrollView.panGestureRecognizer.enabled = YES;
}
NSLog(#"pan");
}
If you don't really need the overlay you can solve this with just gesture recognizers. I wrote this up as a test:
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
_scrollView.contentSize = CGSizeMake(self.view.bounds.size.width * 2, self.view.bounds.size.height);
UIView *green = [[UIView alloc] initWithFrame:self.view.bounds];
[green setBackgroundColor:[UIColor greenColor]];
UIView *blue = [[UIView alloc] initWithFrame:CGRectOffset(self.view.bounds, self.view.bounds.size.width, 0)];
[blue setBackgroundColor:[UIColor blueColor]];
[_scrollView addSubview:green];
[_scrollView addSubview:blue];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(twoFingerPan:)];
[pan setMinimumNumberOfTouches:2];
[pan setMaximumNumberOfTouches:2];
[pan setDelaysTouchesBegan:YES];
[_scrollView addGestureRecognizer:pan];
[self.view addSubview:_scrollView];
}
- (void)twoFingerPan:(UIPanGestureRecognizer *)gesture {
switch (gesture.state) {
case UIGestureRecognizerStateBegan:
self.scrollView.scrollEnabled = NO;
break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateFailed:
self.scrollView.scrollEnabled = YES;
break;
default:
break;
}
NSLog(#"2 Fingers!");
}
I get the twoFingerPan: call back for when 2 fingers are used. The scroll view's panGestureRecognizer is still working at that point so I disable scrolling on the scroll view to handle the 2 finger pan. I've found this method work's pretty well. One sort of wonky thing is if the scroll view is decelerating the 2 finger gesture recognizer isn't called. Hope that helps!