Manage long press on UITextfiled without disabling context menu? - ios

I've UITableView and its cell have one UITextField.
Now I added long gesture in UITextField but it not working. When I tap long gesture on textfield it always show context menu (select,copy cut,past,etc.).
My question is that how to manage long gesture as well as context menu in UITextFiled.
I've tried below code:
longGesture = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
longGesture.minimumPressDuration = 2.0; //seconds
longGesture.delegate = self;
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
CGPoint p = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
if (indexPath == nil) {
NSLog(#"long press on table view but not on a row");
} else if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
NSLog(#"long press on table view at row %ld", indexPath.row);
} else {
NSLog(#"gestureRecognizer.state = %ld", gestureRecognizer.state);
}
}
Tableview delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Note *obj = [self.dataArr objectAtIndex:indexPath.row];
TableViewCell *Cell = [self.tableView dequeueReusableCellWithIdentifier:#"cell"];
if (Cell == nil) {
Cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
Cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
else{
Cell.memoField.text = obj.memoRowText;
}
Cell.memoField.userInteractionEnabled = YES;
[Cell.memoField addGestureRecognizer:longGesture];
Cell.memoField.delegate = self;
Cell.memoField.tag = indexPath.row;
return Cell;
}

You'll want to set up a failure requirement between the gesture that shows the context menu and your long press gesture. Specifically, you'll want the menu recognizer to require your long press to fail (i.e. you'll want the menu recognizer to wait until it has ruled out the long press). In code, one way to do that is this is implementing this delegate method.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)longPress shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)other {
if (other.view /* is a text field in the table view */) {
return YES;
} else {
return NO;
}
}
These methods can be a little confusing.
Remember that you can add "static" failure requirements with -[UIGestureRecognizer requireGestureRecognizerToFail:], but in many cases you don't necessary easily have references to both recognizers (such as in this case).
In many cases, this suffices.
However, the gesture recognizer system also gives you a chance to install failure requirements "on the fly".
Returning YES from -gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer: has the same effect as if you called [second requireFailureOfGestureRecognizer:first] (where first and second are the first and second arguments to that method).
OTOH returning YES from -gestureRecognizer:shouldRequireFailureOfGestureRecognizer: has the same effect as if you called [first requireFailureOfGestureRecognizer:second].

Related

How to detect tap only on cells not on whole collection view?

I have collectionView in my app. I have a requirement that I should be able to both double & single tap on cells to perform diffrent operations. To make it possible both double & single tap gesture on the collectionView I have added both the gesture on collection view & got the location by below code.
-(void)handleSingleTap:(UITapGestureRecognizer *)gestureRecognizer
{
if([arr_userAlbums count]>0)
{
if (gestureRecognizer.state != UIGestureRecognizerStateEnded)
{
return;
}
p = [gestureRecognizer locationInView:self.collection_view];
NSIndexPath *indexPath = [self.collection_view indexPathForItemAtPoint:p];
celltTapped_index_path=indexPath;
}
}
-(void)handleDoubleTap:(UITapGestureRecognizer *)gestureRecognizer
{
if([arr_userAlbums count]>0)
{
if (gestureRecognizer.state != UIGestureRecognizerStateEnded)
{
return;
}
p = [gestureRecognizer locationInView:self.collection_view];
NSIndexPath *indexPath = [self.collection_view indexPathForItemAtPoint:p];
celltTapped_index_path=indexPath;
}
}
But in this case the whole screen even where cells are not visible it is accepting the double tap & single tap. I want to detect single & double tap only on cells not the whole collection view.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"cvCell";
customCell *cell = (customCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
cell.img_Collection.image = [imgArray objectAtIndex:indexPath.row];
cell.lbl_Collection.text = [lblArray objectAtIndex:indexPath.row];
cell.tag = indexPath.row;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTapsRequired = 1;
singleTap.delaysTouchesEnded = YES;
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
doubleTap.numberOfTapsRequired = 2;
[singleTap requireGestureRecognizerToFail:doubleTap];
[cell addGestureRecognizer:singleTap];
[cell addGestureRecognizer:doubleTap];
return cell;
}
-(void)handleSingleTap:(UIGestureRecognizer *)recognizer
{
NSLog(#"The single tap happened for %ld th index",recognizer.view.tag);
}
-(void)handleDoubleTap:(UIGestureRecognizer *)recognizer
{
NSLog(#"The Double tap happened for %ld th index",recognizer.view.tag);
}
you can get tapped View by using hitTest Method like this
UIView *tappedView = [self hitTest:yourLocation forEvents:nil];
This will return you the view which is tapped
Check this with if condition like this
if(tappedView == Imageview)
{
// do this
}
else if(tappedView == CollectionViewCell)
{
// do this
}

Change Selection of Cell on Long Press uiTableView IOS

Normal selection of cell on uitableview is working fine. But when long press event is called for a cell it selects the previously selected cell again. For example if user selected 1st cell then long press the second cell ,, event for long press is called for the second cell but selection goes back again to first cell.
this is my codes:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
//add longPressGestureRecognizer to your cell
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
//how long the press is for in seconds
lpgr.minimumPressDuration = 1.0; //seconds
[cell addGestureRecognizer:lpgr];
}
return cell;
}
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
CGPoint p = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
if (indexPath == nil) {
NSLog(#"long press on table view but not on a row");
}
else
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
NSLog(#"long press on table view at row %ld", (long)indexPath.row);
editViewController *editView = [self.storyboard instantiateViewControllerWithIdentifier:#"editView"]; //dont forget to set storyboard ID of you editViewController in storyboard
[self.navigationController pushViewController:editView animated:YES];
}
}
}
It seems that the issue you are having is rooted in the fact that marking a cell as selected and handling a long press gesture event on that cell are separate. My interpretation of your question is that you have a table view with single selection (not multiple) and that you want the cells to become 'selected' both via the normal tap to select action and also by recognizing a long press gesture. However -- while you want the cell to become selected with the long press gesture, you would like the longpress to result in a different action than selection via a normal tap (e.g. if the user taps a cell you want to launch view controller A but if the user long presses that cell you want to launch view controller B, and in both cases you want the table to regard the cell as 'selected) ...let me know if what you want is different than this, and I can update the answer.
This isn't a very common behavior for table views, but we can make it work by modifying your code a bit:
First define a property to keep track of whether a long press is happening:
#property (assign, nonatomic) BOOL longPressActive;
Then in your handleLongPress method, tell the table to select the row:
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
CGPoint p = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
if (indexPath == nil) {
NSLog(#"long press on table view but not on a row");
}
else
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
NSLog(#"long press on table view at row %ld", (long)indexPath.row);
self.longPressActive = YES;
[self.tableView selectRowAtIndexPath:indexPath
animated:NO
scrollPosition:UITableViewScrollPositionNone];
}else if (gestureRecognizer.state == UIGestureRecognizerStateEnded ||
gestureRecognizer.state == UIGestureRecognizerStateCancelled) {
self.longPressActive = NO;
}
}
Finally, in your table view delegate methods, define the behavior you expect after selection. Note that in the is example a long press on any cell will result in the same view controller displaying. In order to set that view controller up differently you can follow a process similar to my answer in your prior question or you can pass row specific data to the editViewController after you instantiate it.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.longPressActive) { //Perform action desired when cell is long pressed
editViewController *editView = [self.storyboard instantiateViewControllerWithIdentifier:#"editView"];
[self.navigationController pushViewController:editView animated:YES];
}else { //Perform action desired when cell is selected normally
//Your code here
}
}
Hopefully that's helpful.

indexPath of tableViewCell [duplicate]

This question already has answers here:
Getting row of UITableView cell on button press
(10 answers)
Closed 9 years ago.
I have a custom method to detect a tap on a cell's image. I want to also find the index path of the image's correlating cell, and use it within the function. Here is what I am using:
CellforRowAtIndexPath:
UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(cellImageTapped:)];
tapped.numberOfTapsRequired = 1;
[cell.imageView addGestureRecognizer:tapped];
method im trying to get the index path at:
-(void)cellImageTapped:(id)sender {
if(videoArray.count > 0){
Video *currentVideo = [videoArray objectAtIndex:INDEX_PATH_OF_CELL_IMAGE];
//do some stuff
}
}
I have no idea how to pass the index path. Any ideas?
Simple way:
Get the point of touch
Then get index path of cell at point
The code is:
-(void)cellImageTapped:(id)sender {
UITapGestureRecognizer *tap = (UITapGestureRecognizer *)sender;
CGPoint point = [tap locationInView:theTableView];
NSIndexPath *theIndexPath = [theTableView indexPathForRowAtPoint:point];
if(videoArray.count > 0){
Video *currentVideo = [videoArray objectAtIndex:theIndexPath];
//do some stuff
}
}
I would recommend this way to fetch indexPath of cell which has custom subview - (compatible with iOS 7 as well as all previous versions)
- (void)cellImageTapped:(UIGestureRecognizer *)gestureRecognizer
{
UIView *parentCell = gestureRecognizer.view.superview;
while (![parentCell isKindOfClass:[UITableViewCell class]]) { // iOS 7 onwards the table cell hierachy has changed.
parentCell = parentCell.superview;
}
UIView *parentView = parentCell.superview;
while (![parentView isKindOfClass:[UITableView class]]) { // iOS 7 onwards the table cell hierachy has changed.
parentView = parentView.superview;
}
UITableView *tableView = (UITableView *)parentView;
NSIndexPath *indexPath = [tableView indexPathForCell:(UITableViewCell *)parentCell];
NSLog(#"indexPath = %#", indexPath);
}
Add a tag to the UIImageView in your UITableViewDataSource's tableView:cellForRowAtIndexPath: method.
cell.imageView.tag = indexPath.row;
I ended up using the sender's view's tag. Hopefully this will help someone, as I wasted an hour finding the answer.
-(void)cellImageTapped:(id)sender {
UITapGestureRecognizer *gesture = (UITapGestureRecognizer *) sender;
if(videoArray.count > 0){
NSInteger datIndex = gesture.view.tag;
Video *currentVideo = [videoArray objectAtIndex:datIndex];
}
}
Use the delegate method didSelectRowAtIndexPath: method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self cellImageTapped:indexPath];
}
Then you can pass the index to a function i.e.
-(void)cellImageTapped:(NSIndexPath *)indexPath
{
Video *currentVideo = [videoArray objectAtIndex:indexPath.row];
}

Static table view cells seem to have the same data and cell selected index?

Basically I need two things to work with the cells in my view, a tap and a tap and hold gesture. I have the tap and hold gesture working like so:
-(void) longTap:(UILongPressGestureRecognizer *)gestureRecognizer
{
NSLog(#"gestureRecognizer= %#",gestureRecognizer);
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan)
{
NSLog(#"longTap began");
CGPoint p = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [myTable indexPathForRowAtPoint:p];
if (indexPath == nil)
{
NSLog(#"long press on table view but not on a row");
}
else
{
NSLog(#"long press on table view at row %d", indexPath.row);
switch (indexPath.row)
{
case 0:
del.tableRowNumber = 0;
break;
case 1:
del.tableRowNumber = 1;
break;
case 2:
del.tableRowNumber = 2;
break;
case 3:
del.tableRowNumber = 3;
break;
case 4:
del.tableRowNumber = 4;
break;
case 5:
del.tableRowNumber = 5;
break;
}
}
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"MealPlannerRecipeTypeViewController"];
[self.navigationController pushViewController:controller animated:YES];
}
}
I need this gesture to set a value in a singleton class to a certain value depending on what row is selected. No matter what row is selected this value is always 0?! Can anyone tell me why?
Second part of the question is that one of my tableview delegates looks like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
RecipeInfo *recipeInfo = recipeInfoArray[indexPath.row];
cell.textLabel.text = recipeInfo.name;
// Add long tap for the main tiles
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longTap:)];
[cell addGestureRecognizer:longPressGesture];
}
return cell;
}
Every row in my table view has the same information as the first one? Why is this?
Thanks
Since you're using reusable cells, and you've changed the textLabel inside the if() alloc-init block.
Move the cell.textLabel.text line out of the if() { } block right above return cell;
if (cell == nil) {
cell = ...
}
RecipeInfo *recipeInfo = recipeInfoArray[indexPath.row]
cell.textLabel.text = recipeInfo.name;
return cell;
}
EDIT: The above was for question 2.
The first question, about your gesture recognizer...looking at your code, you have
[gestureRecognizer locationInView:self.tableView];
But later you write
[myTable indexPathForRowAtPoint
Did you think to change locationInView:self.tableView to locationInView:myTable
You set cell values only when you created it. The "dequeue" can return a already instantiated cell. I purpose that move if bracket.
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
// Add long tap for the main tiles
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longTap:)];
[cell addGestureRecognizer:longPressGesture];
}
RecipeInfo *recipeInfo = recipeInfoArray[indexPath.row];
cell.textLabel.text = recipeInfo.name;
...

iOS - indexPathForRowAtPoint don't return correct indexPath with different cell height

I have UITableView that contains many cell. User can expand cell to see more content in this cell by push the expand button in this cell (only 1 cell can expand at time):
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectedRowIndex == indexPath.row) return 205;
else return 60;
}
In the storyboard, I drag UILongPressGesture into cell button and named it longPress (cell is custom, it has 2 buttons in it, 1 need to recognize LongPressGesture, the other expand cell height):
#property (retain, nonatomic) IBOutlet UILongPressGestureRecognizer *longPress;
And in the viewDidLoad:
- (void)viewDidLoad
{
[longPress addTarget:self action:#selector(handleLongPress:)];
}
It's work perfectly, however when I use following code to recognize cell indexPath, it's wrong when one cell is expanded:
- (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
// Get index path
slidePickerPoint = [sender locationInView:self.tableView];
NSIndexPath *indexPath= [self.tableView indexPathForRowAtPoint:slidePickerPoint];
// It's wrong when 1 cell is expand and the cell's button I hold is below the expand button
}
Can anyone please show me how to get correct indexPath when there're different cell height?
Thank in advance
One way to do it would be to add a UILongPressGestureRecognizer to each UITableViewCell (that all use the same selector), then when the selector is called you can get the cell via sender.view. Perhaps not the most memory efficient, but if the single gesture recognizer won't return the right row in certain situations, this way should work.
Something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
[longPress setMinimumPressDuration:2.0];
[cell addGestureRecognizer:longPress];
[longPress release];
return cell;
}
then
- (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
UITableViewCell *selectedCell = sender.view;
}
First add the long press gesture recognizer to the table view:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
lpgr.minimumPressDuration = 2.0; //seconds
lpgr.delegate = self;
[self.myTableView addGestureRecognizer:lpgr];
[lpgr release];
Then in the gesture handler:
-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
CGPoint p = [gestureRecognizer locationInView:self.myTableView];
NSIndexPath *indexPath = [self.myTableView indexPathForRowAtPoint:p];
if (indexPath == nil)
NSLog(#"long press on table view but not on a row");
else
NSLog(#"long press on table view at row %d", indexPath.row);
}
}
You have to be careful with this so that it doesn't interfere with the user's normal tapping of the cell and also note that handleLongPress may fire multiple times before user lifts their finger.
Thanks...!

Resources