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
}
}
Related
I have a small UITableView that is hidden when the view is loaded. When i click on "SHOW" UIButton, the UITableView is made visible by myTableView.hidden=NO;
I want to hide UITableView when a user touches outside its frame. Thanks for any help!
Best Approach
Simple.Before show up the UITable View add one more grayed out/Transparent view then add tap gesture recogniser on it to hide it . That's it.
Show Overlay View first - alpha will be 0.5f and background color should be clear color.
show the table view.
NOTE: over lay view should have tap recogniser which will hide the overlay and table view
in View did load
UITapGestureRecognizer *tapRecog = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(overlayDidTap:)];
[myOverLayView addGestureRecognizer:tapRecog];
- (void)overlayDidTap:(UITapGestureRecognizer *)gesture
{
//hide both overlay and table view here
}
Bad Approach
We should not add tap recogniser on main view itself. Because it may have lots of
controls inside of it. so when user tap on it. it will perform its operation. So to avoid
it we can simulate the same behaviour by above approach
You can get touch position by this:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureCaptured:)];
[self.view addGestureRecognizer:singleTap];
- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
CGPoint touchPoint=[gesture locationInView:self.View];
}
Then just check if the point is in tableview frame. If not then hide the tableview. Hope this help. :)
Subclass UITableView, override pointInside:withEvent:. It is templated for this reason.
Something like:
-(BOOL)pointInside:(CGPoint) point withEvent:(UIEvent*) event
{
BOOL pointIsInsideTableView = [super pointInside:point withEvent:event];
BOOL pointIsOutsideTableView = // Some test
return pointIsInsideTableView || pointIsOutsideTableView;
}
So you can catch the touch in table view implementation, where it belongs logically in this case.
I have a PageControl and each page (ViewController) has different number of imageviews (UIImageView), which are created dynamically.
Each imageview is assigned a gesture recognizer (tapped or move). Since these are inside a PageControl, I would like to enable/disable the gesture recognizer so it won't interfere with the swipe to page events.
I know that there's a removeGestureRecognizer method, but I don't want to remove and attach that each time. Is there an equivalent for just enabling and disabling?
Thanks
You can use enable or disable properties of the UIGestureRecognizer like :
swipeGestureRecognizer.enabled = NO;
or you can use the gesture recognizer method return null if you don't want touches
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch;
If you want to disable all the gestures at the same time, you can do like
imageView.userIntractionEnabled = NO;
if you want to disable only one gesture recognizer, then
NSArray *gestures = imageView.gestureRecognizers;
for(UIGestureRecognizer *gesture in gestures)
{
if([gesture isKindOfClass: [UITapGestureRecognizer class]])
{
gesture.enabled = NO;
}
Can you disable userInteractionEnabled for that UIImageView ? You could do it in Interface Builder if you are doing it that way or you could programatically set this like so - imageView.userInteractionEnabled = NO; Hope this helps...
I want to achieve the following.
Scenario: The iOS keyboard is on-screen while the user is typing into a particular text field. The user can tap anywhere outside of the keyboard and text field to dismiss the keyboard (without activating any buttons which are visible). Also, the user can drag outside of the keyboard and observe the normal drag behavior on some arrangement of scrollable views.
Conceptually, I’m placing a “cover” UIView over most of the screen which behaves such that:
If the user taps on the cover, then I capture that tap (so that I can, e.g., dismiss the keyboard). This is easy to achieve by intercepting touch events in a UIView subclass or using a tap gesture recognizer.
If the user drags on the cover, then the cover ignores or forwards these touches; these are received by the layers underneath just as they would have been without a cover.
So: the user should be able to scroll content underneath the cover, but not tap content underneath the cover. A tap “outside” of the keyboard and text field should dismiss the keyboard (and cover), but should not activate anything.
How can I achieve this?
Add the tap gesture the usual way:
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapAction:)];
[self.view addGestureRecognizer:tapGesture];
But what you may be looking for is this :
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Documentation says : This method is called when recognition of a gesture by either gestureRecognizer or otherGestureRecognizer would block the other gesture recognizer from recognizing its gesture. (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIGestureRecognizerDelegate_Protocol/index.html#//apple_ref/occ/intf/UIGestureRecognizerDelegate)
This way, you may be sure it's totally transparent, and also that nothing will prevent your recognizer from being called.
A custom view which forwards all touches it receives:
class CustomView: UIView {
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
var hitView = super.hitTest(point, withEvent: event)
if hitView == self {
return nil
}
return hitView
}
}
From there you can go different ways to just make use of tap gestures. Either observe the UIEvent for its touches, or use a gesture recognizer.
1: Add a tap gesture recognizer to the view:
//Adding tap gesture
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapGesture:)];
tapGesture.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapGesture];
2: In handleTapGesture you resignFirstResponder of the keyboard
- (void)handleTapGesture:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateRecognized) {
//Resign first responder for keyboard here
}
}
Elaborated a bit on the answer above. UIGestureRecognizerStateRecognized makes sure it's single tab events that gets recognized.
Is this the functionality you where after?
I have a textfield inside a UIScrollView and i want to show a clear button when user starts editing. Also i need to hide keyboard when user taps the background of UIScrollview (but not the textfield). Displaying that clear button isn't a problem, the problem is that when clear button is tapped keyboard gets hidden and the text field doesn't get cleared. Obviously the problem is with the gesture recognizer, because method dealing with this gets fired when the clear button is clicked (but it's not fired when the text field is tapped). Here's my code :
//adding gesture recognizer so i can hide keyboard when user taps scrollview
- (void) textFieldDidBeginEditing:(UITextField *)textField
{
if (self.tapOutside == nil) self.tapOutside = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(textFieldTouchOutSide:)];
[self.scrollView addGestureRecognizer:self.tapOutside];
}
//This hides keyboard BUT IS ALSO CALLED WHEN CLEAR BUTTON IS TAPPED
- (void)textFieldTouchOutSide:(id)sender
{
[self.textfield resignFirstResponder];
}
//NEVER GETS CALLED
- (BOOL) textFieldShouldClear:(UITextField *)textField {
return YES;
}
Any ideas how to solve this? Maybe better way to add gesture recognizer? I can't think of no elegant solution ... Thanks a lot in advance...
I had the same problem and solved it implementing the following method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of gestures in unwanted elements
if ([touch.view isMemberOfClass:[UIButton class]]) { // The "clear text" icon is a UIButton
return NO;
}
return YES;
}
Don't forget to conform to the "UIGestureRecognizerDelegate" protocol and set the delegate (using your vars):
self.tapOutside.delegate = self;
Cheers
I was just having this issue and this solution worked, however if you do have other buttons on the view that you allow the user to tap while filling out the form you can do the following:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of gestures in unwanted elements
if ([touch.view isMemberOfClass:[UIButton class]] && [touch.view.superview isMemberOfClass:[UITextField class]]) {
// The "clear text" icon is a UIButton
return NO;
}
return YES;
}
This will narrow down the case to only return No if the button is a subview of a UITextField, as is the case with the clear button, but still hide the keyboard if they touch a normal button that would normally execute your gesture code.
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