I have created a page-based application in Xcode 4, for iPad iOS5.
When I run the app, I can see the pages in the book and can flip them back and forward,
by tap on the screen or by moving the finger from left to right, or right to left.
My problem is that no matter where I'm pressing in the screen, in the borders, the page turns.
I had managed to cancel the flip with fingers with this code:
for (UIGestureRecognizer *gR in self.pageViewController.gestureRecognizers)
{
if ([gR isKindOfClass:[UIPanGestureRecognizer class]])
{
[[gR view] removeGestureRecognizer:gR];
}
}
How can I define a specific area in the screen that when I tap on it, and only it, the page will turn?
I ask this because I put toolbar in the bottom of the screen and when I click on a button in the toolbar the page flips. I want to put 2 arrows on the screen that only when I press on them the page will flip.
Sorry if my explanation is a little bit rusty. Thank you all.
Cipramill's answer is correct -- here are more details.
The IOS documentation suggests adding new Views to delineate the areas where you wish the page turning gestures to be active, but this method is much simpler. Adding code to the default template set up by Xcode 4 in MQ1RootViewController.h and MQ1RootViewController.m:
Change interface line in MQ1RootViewController.h:
#interface MQ1RootViewController : UIViewController <UIPageViewControllerDelegate,
UIGestureRecognizerDelegate>
Add this code to the very bottom of viewDidLoad in MQ1RootViewController.m:
for (UIGestureRecognizer *gR in self.pageViewController.gestureRecognizers) {
gR.delegate = self;
}
Add this method to MQ1RootViewController.m:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]
|| [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGPoint point = [touch locationInView:self.view];
if(point.x < 100 || point.x > 924) return YES;
}
return NO;
}
Note that the "swipe" gesture is actually derived from a "pan" gesture by the page view controller object.
The above limits the gestures to the left and right edges of the screen. This allows gestures to be used to interact with objects in the center of the screen without accidentally changing the page with an errant swipe.
you could hook into the the gesture system and define which area to accept touches for.
In this explanation I assume your RootViewController has a UIPageViewController as a child VC:
-Set your root view controller to implement UIGestureRecognizerDelegate
-Take over all gesture recognizers for your pageVC in your RootViewControllers ViewDidLoad:
for (UIGestureRecognizer *gR in self.pageVC.gestureRecognizers) {
gR.delegate = self;
}
-Finally implement the gesture recognizer in your RootViewController and define which zones you want to ignore:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
CGPoint point = [touch locationInView:self.view];
//Examine point and return NO, if gesture should be ignored.
}
return YES;
}
Hope this helps
Related
I am modifying a module which is basically a standard hidden left menu (swipe to the right to reveal the menu)
The standard behavior of this menu system is that any swipes to the left are blocked, thereby limiting the ability to swipe left to delete a row in a tableview for example.
I've added this code in which successfully opens up the ability to swipe to the left to delete a row in a table.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if([otherGestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]])
{
}
return YES;
}
Problem is, that it now also allows the menu to open up on any type of swipe to the right. So for example, if you are scrolling down, but happen to scroll down at a slight angle to the right themenu opens up willy nilly. Its very annoying
The Question
What would be the proper piece of code to add in here to block swipe right gestures?
It seems the 'return YES' is what is opening up the additional gestures but am struggling to find a way to return NO on a swipe right. The original code actually had this other part, but it didn't seem to do anything different.
To my eyes, it is the 'RETURN YES' that is opening up the behavior. If I could find a way to RETURN NO on a swipe to the right that might do it?
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if([otherGestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]])
{
UISwipeGestureRecognizer *recog = (UISwipeGestureRecognizer *)otherGestureRecognizer;
if(recog.direction == UISwipeGestureRecognizerDirectionLeft && [recog.view isKindOfClass:[UITableViewCell class]]) return NO;
}
return YES;
}
You can try this trick. Add a new UIGestureRecognizer to your UITableView and add a selector to that with nothing inside.
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(myMethod:)];
swipe.direction = UISwipeGestureRecognizerDirectionRight;
[self.tableView addGestureRecognizer:swipe];
Of course for the gestureRecognizer's selector:
- (void)myMethod {}
I am using iCarouselButtonDemo to create an arc button menu. I want to disable the scrolling when user touch the space other than the buttons. But now we can scroll the view by touching every point of the UIView. How can I detect the touch point of the view and disable the scrolling when user touch the outside of the 5 buttons
This is my view. This is scrolling when I touch even the bottom of the view. How can I stop it?
Thanks
In the iCarousel implementation file, you will add the following code in gestureRecognizerShouldBegin method. So it looks like this. It firstly get the touch point in the iCarousel view, and find the inner most view responding to the touch through hitTest. If the view is not a button, you stop the pan gesture.
if ([gesture isKindOfClass:[UIPanGestureRecognizer class]])
{
CGPoint point = [gesture locationInView:self];
UIView *touchedView = [self hitTest:point withEvent:nil];
if (![touchedView isKindOfClass:[UIButton class]]) {
return NO;
}
//ignore vertical swipes
I am using ECSlidingViewController for hamburger menu and I added code to my viewDidLoad method:
[self.slidingViewController.topViewController.view addGestureRecognizer:self.slidingViewController.panGesture];
Now I have pan gesture to show right menu or to hide. It's okay. But I have on view slider and it's really hard to get him working. I must tap on exact position. Is it possible to set that in exact rectangle (in view that contains slider) the slider would be answering on gesture and on other parts it would be working as now?
And one more question. When I have navigation controller with table and then I went on detail and then I show right menu it's okay but when I want to close it by pan I first go back in navigation and then close menu. Is it possible to change this order?
Have you tried setting UIGestureRecognizerDelegate and handling the both gesture recognizes in similar fashion as described in FAO?
e.g.:
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([otherGestureRecognizer.view isKindOfClass:[UISlider class]]) {
return YES;
} else {
return NO;
}
}
I have a simple MapKit app working fine in iOS. It has annotation and when the user clicks on them, the little gray default popup is displayed with the title / subtitle. I even added a UIButton view into it.
So the problem is, I have a search bar above my map. I wanted to resignFirstResponder from the search box whenever the user clicks on the MapView, so I added a simple tap gesture responder. Worked great except now the little gray detail popups no longer show up (only the annotation pins)! I can still tap, zoom, move around etc. Just no popups.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
tap.cancelsTouchesInView = NO;
tap.delaysTouchesBegan = NO;
tap.delaysTouchesEnded = NO;
[mapView addGestureRecognizer:tap];
-(IBAction)tapped:(UITapGestureRecognizer *)geture {
[searchBar resignFirstResponder];
}
Is it possible to have the best of both worlds?
I used a delegate method similar to the following to arbitrate between touches that should go to my custom view's pan gesture recognizer and touches that should go to the scroll view that contained my custom view. Something like it might work for you.
// the following UIGestureRecognizerDelegate method returns YES by default.
// we modify it so that the tap gesture recognizer only returns YES if
// the search bar is first responder; otherwise it returns NO.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ((gestureRecognizer == self.tapGestureRecognizer) &&
(gestureRecognizer.view == self.mapView) &&
[searchBar isFirstResponder])
{
return YES; // return YES so that the tapGestureRecognizer can deal with the tap and resign first responder
}
else
{
return NO; // return NO so that the touch is sent up the responder chain for the map view to deal with it
}
}
I have a 2d map that the user can zoom and pan using the gesture recognizers. While it works, i want the user to start panning immediately after a zoom once they have 1 finger lifted. Unfortunately, in the docs it says:
The gesture ends (UIGestureRecognizerStateEnded) when both fingers
lift from the view.
which is pretending me from going from a pinch zoom to a pan right away. What could i do to fix this?
This is possible, and easy! It involves being your gesture recognizer's delegate. Something no-one seems to know exists. In my view controller subclass I have declared both conforming to the protocol <UIGestureRecognizerDelegate> and two ivars:
UIPinchGestureRecognizer *myPinchGR;
UIPanGestureRecognizer *myPanGR;
These ivars are instantiated in view did load. Notice setting self as the delegate.
-(void)viewDidLoad{
[super viewDidLoad];
myPanGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panTarget:)];
myPanGR.delegate = self;
[self.view addGestureRecognizer:myPanGR];
myPinchGR = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(pinchTarget:)];
myPinchGR.delegate = self;
[self.view addGestureRecognizer:myPinchGR];
}
One of the delegate calls made by a UIGestureRecognizer is shouldRecognizeSimultaneouslyWithGestureRecognizer: if I had more than two gesture recognizers then this function would have to contain some logic. But since there are only two I can just return YES.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
Now you do have to include a little (very little) extra logic in your action methods to screen for the appropriate conditions.
-(void)panTarget:(UIPanGestureRecognizer *)panGR{
if (panGR.numberOfTouches > 1) return;
NSLog(#"panny");
}
-(void)pinchTarget:(UIPinchGestureRecognizer *)pinchGR{
if (pinchGR.numberOfTouches < 2) return;
NSLog(#"pinchy");
}
Run this code an look at the logs. you will see when you move one finger you will see "panny" when you place a second finger down you will see "pinchy", and back and forth.
Use this code inside the gesture handling method.
if (gesture.numberOfTouches != 2) {
// code here to end pinching
}
As Gesture handling method will be called immediately when user lift a finger while 2 finger pinching.