I have an issue with the UITapGestureRecognizer in my collection view and I don't know the error.
I want to do a custom action when there is a long press gesture, and when there is a tap gesture I want to do nothing, so I have these methods:
- (void)activateSelectionMode:(UILongPressGestureRecognizer *)gr
{
if (![self.collectionView allowsSelection]) {
[self.collectionView setAllowsSelection:YES];
NSLog(#"Seleccion activada");
}
}
- (void)pruebaTap:(UITapGestureRecognizer *)tr
{
NSLog(#"tap");
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
CGPoint touchPoint = [touch locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:touchPoint];
if (indexPath != nil && [gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
{
CVCell *cell = (CVCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
if ([[cell checkImage] isHidden]) {
// TODO: Añadir la celda a la lista de celdas seleccionadas
[[cell checkImage] setHidden:NO];
NSLog(#"Seleccionada celda %#", [[cell titleLabel] text]);
} else {
// TODO: Quitar la celda de la lista de celdas seleccionadas
[[cell checkImage] setHidden:YES];
NSLog(#"No seleccionada celda %#", [[cell titleLabel] text]);
}
NSLog(#"Entra");
return YES;
}
return NO;
}
IF I comment the last method, each method is recognized perfectly, but if I don't comment the last method, the tap gesture is recognized as long press gesture. Here I assign the gesture to the collection view:
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(pruebaTap:)];
tap.delegate = self;
[self.collectionView addGestureRecognizer:tap];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(activateSelectionMode:)];
longPress.delegate = self;
[self.collectionView addGestureRecognizer:longPress];
Thanks so much in advance.
Not sure weather you have implemented the below gesture delegate method or not.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer
:(UIGestureRecognizer *)otherGestureRecognizer;
If you have not implemented then no problem because default implementation returns NO but if you have implemented & returned YES then both the gesture will be recognized. May be returning NO will resolve your problem
It will definitely recognize long press gesture because , you have added it last, what you are doing is , you are adding 2 gestures on same view, so here your longPress gesture will overlap on UITapGestureRecognizer gesture(that is tap), so everytime longpress gesture will be called.
what you can do is , you will have to add one at a time.
Related
I have a View which is the subView of the main view in iphone app I want that when subView is shown and user taps on part of the screen except the subView then subView should hide.
Here is the code which I got but it does not hide it :
UITapGestureRecognizer *tapGR;
tapGR = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)] autorelease];
tapGR.numberOfTapsRequired = 1;
[self.View addGestureRecognizer:tapGR];
// Add a delegate method to handle the tap and do something with it.
-(void)handleTap:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded) {
// handling code
[myViewSheet removeFromSuperView];
}
}
implement UIGestureRecognizerDelegate protocol:
.h:
#interface YourView : UIView<UIGestureRecognizerDelegate>
.m:
#implementation YourView{
UIView * subview;
}
...
subview = [[UIView alloc]initWithFrame:CGRectMake(0, 200, 320, 200)];
[self addSubview:subview];
UITapGestureRecognizer *tapGR;
tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tapGR.numberOfTapsRequired = 1;
tapGR.delegate = self;
[self addGestureRecognizer:tapGR];
...
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (touch.view == subView)
{
return NO;
}
else
{
return YES;
}
}
You probably need to set userInteractionEnabled = YES on your main view (The one to which you are attaching the gesture recognizer.)
I have created a UITableView with a custom UITableViewCell. My cell includes one UIImageView on the left and UITextView on the right.
Inside UITableViewController, I set both image and text in tableview cellForRowAtIndexPath.
Everything shows fine but now I need to implement didSelectRowAtIndex and I need to distinguish if UIImageView or UITextView of the cell has been clicked.
Let's say, image clicking is representing delete action and the rest of the cell editing action.
Rather than adding the gesture recognisers to each individual cell, you can add one to the table view and determine which cell was selected from the point of the users touch, and then determine if the user touched the image or the cell.
First make sure your controller adopts the UIGestureRecognizerDelegate protocol.
#interface MyTableViewController() <UIGestureRecognizerDelegate>
#end
Then add the UIGestureRecognizer to the UITableView when the view loads.
- (void)viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
singleTap.delegate = self;
singleTap.numberOfTapsRequired = 1;
singleTap.numberOfTouchesRequired = 1;
[self.tableView addGestureRecognizer:singleTap];
}
This delegate method determines if the handleTap: method should be executed. If it can find an indexPath from the users touch, then it returns YES otherwise it returns NO.
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
UITableView *tableView = (UITableView *)gestureRecognizer.view;
CGPoint p = [gestureRecognizer locationInView:gestureRecognizer.view];
if ([tableView indexPathForRowAtPoint:p]) {
return YES;
}
return NO;
}
Once we have determined if the user has clicked in a cell, the handleTap: method is called, which then decides if the user touched the image, or any other part of the cell.
- (void)handleTap:(UITapGestureRecognizer *)tap
{
if (UIGestureRecognizerStateEnded == tap.state) {
UITableView *tableView = (UITableView *)tap.view;
CGPoint p = [tap locationInView:tap.view];
NSIndexPath* indexPath = [tableView indexPathForRowAtPoint:p];
[tableView deselectRowAtIndexPath:indexPath animated:NO];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
CGPoint pointInCell = [tap locationInView:cell];
if (CGRectContainsPoint(cell.imageView.frame, pointInCell)) {
// user tapped image
} else {
// user tapped cell
}
}
}
You could subclass UITableViewCell and override touchesEnded.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
UIView *hitView = [self hitTest:location withEvent:event];
if (hitView == myImageView) ...;
if (hitView == myTextView) ...;
}
You need to keep some reference to your UIImageView and UITextView (they should probably be properties of your cell).
You can of course override touchesBegan instead of touchesEnded, depends on what functionality you want to achieve.
a very abstract and general answer is to do the following
For each UIImage and UILabel you add set their tag to be the indexPath.row
//When creating the label and image add a recognizer to them
label.tag = indexPath.row;
imageView.tag = indexPath.row;
Then add a UITapGestureRecognizer on each image and label, like so
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleTap:)];
[label addGestureRecognizer:recognizer];
[imageView addGestureRecognizer:recognizer];
}
- (void) handleTap:(UITapGestureRecognizer*)recognizer
{
UIView *view = recognizer.view;
int row = view.tag;
if ([view isKindOfClass:[UILabel class]]) {
//Row is row
//and the label is pressed
}
if ([view isKindOfClass:[UIImageView class]]) {
//Row is row
//and the imageview is pressed
}
}
Make the image a UIButton. When the button's action is triggered, you know the user tapped the image (cell will NOT be selected). If cell is selected, you know the user tapped somewhere else on the row (including text view or label).
Also, set the button's tag property to, e.g. the cell's row index so you can know which row's image was tapped.
you have two option is to implement :-
1--add UITapGestureRecognizer in your "uitableviewcell" and make it point to image view
and pass "indexpath" as parameter to selector and make the delegate pass it to tableviewcell
UILabel *label = =[UILabel alloc]init];
label.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture =
[[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(labelTap)] autorelease];
[label addGestureRecognizer:tapGesture];
-(void)labelTap{
[delegate performselector#selector(labeltapped:)withobject:indexpath];
}
2- Second way is to check the sender of DidSelectRowAtIndexPath of type [imageview or label];
but i prefer the first way
I have this in my table header view section:
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(sectionHeaderTapped:)];
I want to pass the section number in the method sectionHeaderTapped so i can recognize which section got tapped.
My method implementation looks like this:
-(void)sectionHeaderTapped:(NSInteger)sectionValue {
NSLog(#"the section header is tapped ");
}
How can I achieve this?
The method sectionHeaderTapped should have one of the following signatures:
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)sender;
- (void)sectionHeaderTapped;
You have to figure out the cell that was tapped using the coordinates of the tap.
-(void)sectionHeaderTapped:(UITapGestureRecognizer *)gestureRecognizer
{
CGPoint tapLocation = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *tapIndexPath = [self.tableView indexPathForRowAtPoint:tapLocation];
UITableViewCell* tappedCell = [self.tableView cellForRowAtIndexPath:tapIndexPath];
}
You can probably get the section header using that method. But it may be easier to attach a different gesture recognizer to each section header.
- (UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section
{
// ...
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(sectionHeaderTapped:)];
[headerView addGestureRecognizer:tapGesture];
return headerView;
}
And then
-(void)sectionHeaderTapped:(UITapGestureRecognizer *)gestureRecognizer
{
UIView *headerView = gestureRecognizer.view;
// ...
}
An alternate : You can add UIButton on the tableHeaderView and get click of button.
How can I detect when the user taps the selection indicator in a UIDatePicker?
Without this the user has to scroll to some other date and then back again to pick the date which is displayed under the selection indicator when the date picker slides up.
Thanks a lot,
Stine
UPDATE: This is the only solution I could come up with myself:
UIDatePicker *aDatePicker = [[UIDatePicker alloc] init];
self.datePicker = aDatePicker;
[aDatePicker release];
[self.datePicker addTarget:self action:#selector(datePicked:) forControlEvents:UIControlEventValueChanged];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(datePicked:)];
[self.datePicker addGestureRecognizer:tap];
[tap release];
Which means that datePicked will be called twice when the user actually rotates the wheel.
UPDATE: The above mentioned solution does not work for UIPickerViews though. I do not know how to achieve the wanted behavior in those cases.
You can do some tweak in this way:-
Conform delegate <UIGestureRecognizerDelegate>in your .h file
UITapGestureRecognizer* gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(pickerViewTapGestureRecognized:)];
[yourDatePicker addGestureRecognizer:gestureRecognizer];
gestureRecognizer.delegate=self;
gestureRecognizer.numberOfTapsRequired=2;//Whenever you do double tap it will called. So allow user to do double tap on selected date.
//Below is the Delegate method
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
//Below method will trigger when do the double tap
-(void)pickerViewTapGestureRecognized:(UITapGestureRecognizer*)recognizer
{
UIDatePicker *datePicker=(UIDatePicker*)[[recognizer view] viewWithTag:101];
NSLog(#"datePicker=%#", datePicker.date);
}
Try this code:
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(pickerViewTapped:)];
[recognizer setNumberOfTapsRequired:2];
[recognizer setCancelsTouchesInView:NO];
[recognizer setDelaysTouchesEnded:NO];
[recognizer setDelaysTouchesBegan:NO];
[self.answerPicker addGestureRecognizer:recognizer];
// ....
- (IBAction)pickerViewTapped:(UITapGestureRecognizer *)sender {
CGPoint coord = [sender locationInView:self.answerPicker];
if(coord.y <= 126 && coord.y >= 90) {
//do something
}
}
This is an old question but here's what I did in order to grab the selection bar rect on the UIDatePicker. With this, you could just add a button/view with a gesture recognizer to detect taps. It's a bit of a hack but it seems to be working well in iOS6 and iOS7.
+ (CGRect)getSelectionBarRectFromPicker:(UIDatePicker *)picker
{
int counter = 0;
CGRect selectionBarRect;
for(UIView *datePickerView in picker.subviews){
for(UIView *subview in datePickerView.subviews){
if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0){
if([NSStringFromClass([subview class]) isEqualToString:#"_UIPickerViewSelectionBar"]){
if(counter == 0){
selectionBarRect.origin = subview.frame.origin;
selectionBarRect.size.height = subview.frame.size.height;
}
selectionBarRect.size.width += subview.frame.size.width;
counter++;
}
} else {
if(subview.frame.size.height < 1){
if(counter == 0){
selectionBarRect.origin = subview.frame.origin;
selectionBarRect.size.width = subview.frame.size.width;
} else {
selectionBarRect.size.height = subview.frame.origin.y - selectionBarRect.origin.y;
}
counter++;
}
}
}
}
return selectionBarRect;
}
I have a scroll view and a button placed over it , When i add a tap gesture recognizer the button does not work. Is there any way of limiting the tap only to scroll view and not to the button, so that the button functions normally.
here is my code
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesture:)];
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
[scroll addGestureRecognizer:tap];
[tap release];
- (void)tapGesture:(UIGestureRecognizer*)gesture{
NSLog(#"scroll tapped");
}
If you do
tap.cancelsTouchesInView = NO;
It will allow button to be pressed. However taps will be detected along with the button press when you press a button. Do avoid this, you will have to subclass UIScrollView and implement the following method –
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
for ( UIView * subview in self.subviews ) {
UIView * hitView = [subview hitTest:point withEvent:event];
if ( hitView )
return hitView;
}
return [super hitTest:point withEvent:event];
}
Implementing the method above pass the touches to the scroll view's subviews.
There is no need to subclass Scrollview. Following code solves the problem easy way. The method gestureRecognizer:shouldReceiveTouch: does the trick.
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGesture:)];
tap.numberOfTapsRequired = 1;
tap.numberOfTouchesRequired = 1;
tap.delegate = self;
tap.cancelsTouchesInView = NO;
[scroll addGestureRecognizer:tap];
[tap release];
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (scroll.superview != nil) {
if ([touch.view isKindOfClass:[UIButton class]])
{
return NO; // ignore the touch
}
}
return YES; // handle the touch
}
- (void)tapGesture:(UIGestureRecognizer*)gesture {
NSLog(#"scroll tapped");
}