I have a UIView with a UITextField, UIButton and UITable view. The textfield and button compromise a search bar and the results are then loaded into the table view.
I'd like to make it so they keyboard dismisses. If the user taps something when they are editing the text field. My strategy would be to add a gesture recognizer to the UIView, but then gesture recognizer seems to intercept all the touches from the table view and you |tableView:didSelectCellAtIndexPath:| never gets called. Whats interesting (to me at least) is that the UIButton is still tap-able when the user is editing the field even though the UITableView isn't.
I've tried implementing |gestureRecognizer::shouldRecognizeSimultaneouslyWithGestureRecognizer:| to alway return yes, but that doesn't help. I've also tried setting
singleTapRecognizer.cancelsTouchesInView = NO;
which also doesn't help.
I'm happy with the idea of adding and removing the gesture recognizer when the text field calls |textFieldDidBeginEditing:| and |textFieldDidFinishEditing:|, though this feels messy and it still takes two taps to touch a cell when you're editing the text field (one to dismiss they keyboard and remove the recognizer, and one to tap the cell).
Is there a better way?
Relevant code below:
- (void)loadView {
[super loadView];
self.scrollView = [[UIScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.scrollView.backgroundColor = [FDEColors viewBackgroundColor];
self.view = self.scrollView;
self.searchField = [[UITextField alloc] initWithFrame:CGRectZero];
self.searchField.placeholder = #"What are you looking for?";
self.searchField.backgroundColor = [FDEColors textFieldBackgroundColor];
self.searchField.clipsToBounds = YES;
self.searchField.layer.borderColor = [[FDEColors buttonColor] CGColor];
self.searchField.layer.borderWidth = 1.f;
self.searchField.returnKeyType = UIReturnKeySearch;
self.searchField.delegate = self;
[self.view addSubview:self.searchField];
self.searchButton = [[UIButton alloc] initWithFrame:CGRectZero];
self.searchButton.backgroundColor = [FDEColors buttonColor];
[self.searchButton setTitle:#"Search" forState:UIControlStateNormal];
[self.searchButton addTarget:self
action:#selector(searchPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.searchButton];
self.resultsTableView =
[[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
self.resultsTableView.delegate = self;
self.resultsTableView.dataSource = self;
self.resultsTableView.backgroundColor = [FDEColors viewBackgroundColor];
[self.resultsTableView setSeparatorInset:UIEdgeInsetsZero];
self.resultsTableView.layoutMargins = UIEdgeInsetsZero;
[self.resultsTableView registerClass:[FDESearchResultsCell class]
forCellReuseIdentifier:[FDESearchResultsCell reuseIdentifier]];
[self.view addSubview:self.resultsTableView];
self.singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:self.singleTapRecognizer];
}
- (void)dismissKeyboard {
[[self view] endEditing:YES];
}
Try implementing the gesture recognizer's delegate method:
- (BOOL) gestureRecognizer:ShouldReceiveRouch:
In this method, check for the touch's location. If it's inside the tableview, return no, so the tableview can receive the touch. Otherwise, return YES and let the recognizer handle the touch.
Edit: As for the button receiving the touch despite the recognizer's existence, as of iOS6 Apple decided to give buttons and some other controls priority when it comes to recognizing gestures. It only applies to antagonizing gestures though, in your case a single tap. If for example you also had a pan recognizer, the recognizer would have precedence, not the button.
Edit 2:
An example implementation of the method mentioned above:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Determine if the touch is inside the custom subview
if ([touch view] == self.yourTableView){
// If it is, prevent all of the delegate's gesture recognizers
// from receiving the touch
return NO;
}
return YES;
}
Related
I want to make UIImageView respond to user tap. I add image views in storyboard, and set tags to them. Then in viewDidLoad:
UIGestureRecognizer *tapGesture = [[UIGestureRecognizer alloc] initWithTarget:self action:#selector(openCourseView:)];
[self.view viewWithTag:IMAGE_VIEW_TERM].userInteractionEnabled = YES;
[self.view viewWithTag:IMAGE_VIEW_1V1].userInteractionEnabled = YES;
[self.view viewWithTag:IMAGE_VIEW_SPECIAL].userInteractionEnabled = YES;
[[self.view viewWithTag:IMAGE_VIEW_TERM] addGestureRecognizer:tapGesture];
[[self.view viewWithTag:IMAGE_VIEW_1V1] addGestureRecognizer:tapGesture];
[[self.view viewWithTag:IMAGE_VIEW_SPECIAL] addGestureRecognizer:tapGesture];
But my response method openCourseView: never called. Then I tried to use IBOutlet to add gesture recognizer to image view, still not responding.
What's the problem?
It was a mistake found by LokeshChowdary.
In my code, UIGestureRecognizer should be UITapGestureRecognizer.
I'm trying to add UITableView inside UIView but I got problem with gesture recognizer. This is my code:
_subView = [[UIView alloc] initWithFrame:CGRectMake(0, screenRect.size.height-100, screenRect.size.width, 300)];
_subView.backgroundColor = [UIColor colorWithRed:0.110f green:0.192f blue:0.298f alpha:1.00f];
[self.view addSubview:_subView];
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(subViewToggle)];
[_subView addGestureRecognizer:singleFingerTap];
_moreTable = [[UITableView alloc] initWithFrame:CGRectMake(0,50, screenRect.size.width, 240)];
//Transforme to HOR scroll ;
CGRect frame = _moreTable.frame;
_moreTable.transform = CGAffineTransformRotate(CGAffineTransformIdentity, k90DegreesCounterClockwiseAngle);
_moreTable.frame = frame;
[_moreTable registerNib:[UINib nibWithNibName:#"MoreTableViewCell" bundle:nil] forCellReuseIdentifier:#"moreCell"];
_moreTable.backgroundColor = [UIColor groupTableViewBackgroundColor];
_moreTable.delegate = self;
_moreTable.dataSource = self;
[_subView addSubview:_moreTable];
[_subView bringSubviewToFront:_moreTable];
The problem is when I click on table cell instead of executing didSelectRowAtIndexPath method he execute the subViewToggle Method. I tried to bring my tableView to front but didn't help
your tap gesture eat the touch. there are two way you can deal:
first, you can implement - gestureRecognizer:shouldReceiveTouch: delegate method, and decide if tap gesture should occur.
second, you also can set cancelsTouchesInView property of tap to NO, so tableView will receive touch. but tap will receive touch at the some time too.
Try implementing the UIGestureRecognizerDelegate protocol's gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
This should call both your didSelectRow and GestureRecognizer
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIGestureRecognizerDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIGestureRecognizerDelegate/gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
Please try to use like this
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch.view == _moreTable]) {
return NO;
}
return YES;
}
To solve this Problem I've changed the UIView Gesture from single finger tap to moveUP and moveDown gesture.
Note that I've tried only #dharmbir Solution and It haven't worked for me.
Update Also this solution, implementing the shouldReciveTouches, worked for the tapGesture and her's the code snippet :
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// UITableViewCellContentView => UITableViewCell
if([touch.view.superview isKindOfClass:[UITableViewCell class]]) {
return NO;
}
// UITableViewCellContentView => UITableViewCellScrollView => UITableViewCell
if([touch.view.superview.superview isKindOfClass:[UITableViewCell class]]) {
return NO;
}
return YES;
}
//Create tap gesture for menu transparent view
UITapGestureRecognizer *rightTableTransparentViewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(rightTableTransparentViewTapMethod:)];
[rightTableTransparentViewTap setCancelsTouchesInView:NO];
[_rightTableTransparentView addGestureRecognizer:rightTableTransparentViewTap];
- (void)rightTableTransparentViewTapMethod:(UITapGestureRecognizer *)recognizer {
//Write your code here
}
I have an AVPlayerViewController that will have some views on top of it (only when it's in fullscreen). The views have gesture recognizers on them (these work). The problem is that when a user taps on one of the views (they are invisible) the player doesn't also receive the touch... I need the player to show the player controls then..
This is how I add the views
_topHelperView = [[UIView alloc] init];
_topHelperView.backgroundColor = [UIColor purpleColor];
_middleHelperView = [[UIView alloc] init];
_middleHelperView.backgroundColor = [UIColor redColor];
[self setHelperFrames];
_topTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleHelperTapGesture:)];
_topTapGestureRecognizer.delegate = self;
_topTapGestureRecognizer.cancelsTouchesInView = NO;
_middleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleHelperTapGesture:)];
_middleTapGestureRecognizer.cancelsTouchesInView = NO;
_middleTapGestureRecognizer.delegate = self;
I also implemented the delegate:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}
// this enables you to handle multiple recognizers on single view
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
NSLog(#"should Handle simultaneously %# %#",gestureRecognizer,otherGestureRecognizer);
return YES;
}
This doesn't work .. the player doesn't show the controls when either view is tapped.. it only shows them when it's tapped directly ..
Any workaround for this?
Touch events stop when a view (actually a UIResponder) responds to them. So if you have a gesture recognizer attached to a view on top of your AVPlayerViewController, the gesture recognizer will catch the events and the AVPlayerViewController will not. This is by design.
Why not have your view send messages to the AVPlayerViewController in response to the user's gestures? AVPlayerViewController has a property showsPlaybackControls. Set that to YES if you want the player to show its controls.
I have a viewcontroller, which contains a web view.
I'd like to add two gesturerocignizers to the viewController's view:
One UITapGestureRecognizer and one UILongPressureGestureRecognizer.
Here's the code:
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(backToDashboards:)];
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(backToDashboards:)];
doubleTapGesture.numberOfTapsRequired = 2;
doubleTapGesture.delegate = self;
[self.view addGestureRecognizer:longPressGesture];
[self.view addGestureRecognizer:doubleTapGesture];
My view hierarchy:
-ViewController
|
|-View (added the gesture recognizers for this view)
|-WebView
I've added the shouldRecognizeSimultaneouslyWithGestureRecognizer method with return YES
But if I long press the screen nothing happens, only if the press' location is on a place where nothing can be scrolled in the webview.
The double tap recognizer doesn't work at all.
Any idea?
Thank you in advance!
I suspect that because your webview is on top of your view, the app doesnt know if you are tapping the webview or self.view. Check out this reply How to detect of tap gesture is from uiwebview or self.view?
try with below code.Where u r adding gester in you code?
below code is working for me.
- (void)viewDidLoad
{
[super viewDidLoad];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(handleLongPress:)];
longPress.numberOfTouchesRequired = 2;
[self.view addGestureRecognizer:longPress];
// if u want to add gesture for u webview,
//[self.webview addGestureRecognizer:longPress]
}
-(void)handleLongPress:(UILongPressGestureRecognizer *)gesture
{
}
//You should enable simultaneous gesture recognition
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
I am creating an app with cards. In my mainViewController, I have this code:
CardView *cardView = [[CardView alloc] initWithFrame:CGRectMake(0, 0, CardWidth, CardHeight)];
cardView.card = [player.closedCards cardAtIndex:t];
[self.cardContainerView addSubview:cardView];
[cardView animateDealingToBottomPlayer:player withIndex:t withDelay:delay];
delay += 0.1f;
where CardView is a subclass of UIView. Each Card is a unique cardView and in CardView.m I do have:
#implementation CardView
{
UIImageView *_backImageView;
UIImageView *_frontImageView;
CGFloat _angle;
}
#synthesize card = _card;
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
self.backgroundColor = [UIColor clearColor];
[self loadBack];
self.userInteractionEnabled=YES;
}
return self;
}
- (void)loadBack
{
if (_backImageView == nil)
{
_backImageView = [[UIImageView alloc] initWithFrame:self.bounds];
_backImageView.image = [UIImage imageNamed:#"Back"];
_backImageView.contentMode = UIViewContentModeScaleToFill;
[self addSubview:_backImageView];
}
}
and the implementations for other functions .
Since in order to win space, one card is placed on top of the others (half of teh card is visible and the rest is covered by the next card and so on), I want to identify touches on each card.
If I place that code in CardView:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Card is touched");
}
it is never called. If I place it in the GameView Controller it is called anywhere that I will touch, but I do not know how to identify which cardView is called. Can you give me a hint?
EDIT:
I decided to use gestures. Therefore in my mainViewController changed the code to this:
for (PlayerPosition p = startingPlayer.position; p < startingPlayer.position + 4; ++p)
{
CardView *cardView = [[CardView alloc] initWithFrame:CGRectMake(0, 0, CardWidth, CardHeight)];
cardView.card = [player.closedCards cardAtIndex:t];
cardView.userInteractionEnabled=YES;
[self.cardContainerView addSubview:cardView];
[cardView animateDealingToBottomPlayer:player withIndex:t withDelay:delay];
delay += 0.1f;
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(cardSelected:)];
[cardView addGestureRecognizer:recognizer];
}
but this is never called.
-(void)cardSelected:(UITapGestureRecognizer *)recognizer
{
NSLog(#"Card Selected with gestures");
}
Why is that?
EDIT 2:
I have also tried adding this:
self.cardContainerView.userInteractionEnabled=YES;
or changing this:
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(cardSelected:)];
to this:
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self .cardContainerView action:#selector(cardSelected:)];
but none of them worked.
If you instead add a tap gesture recogniser to each card view you can get a callback to a method you specify and the gesture is connected to the view so you can directly get a reference to it.
Your CardView (which I guess is a subclass of UIView?) has 2 subviews, which are image views:
UIImageView *_backImageView;
UIImageView *_frontImageView;
You may want to set userInteractionEnabled to YES on one or both of them (depending on when the card should be tappable and when the subviews are shown and hidden).
You can also act as the delegate of the gesture (if you need to, or just temporarily to debug that the gesture is getting triggered but blocked by something other gesture).