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.
Related
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].
I have a UIImageView inside UITaleViewCell, I added a tap recognizer to the UIImageView.
- (IBAction)ui_tapped:(UITapGestureRecognizer *)sender {
NSIndexPath *indexPath = [CocoaHelper indexPathWithTableView:self.tableView sender:sender.view];
RichMediaViewController *viewController = (RichMediaViewController *)[CocoaHelper viewControllerWithIdentifier:VC_RICH_MEDIA];
Message *message = self.messages[indexPath.row];
[viewController setupWithEntity:message];
[self presentViewController:viewController animated:YES completion:nil];
}
+ (NSIndexPath *)indexPathWithTableView:(UITableView *)tableView sender:(id)sender {
CGPoint hitPoint = [sender convertPoint:CGPointZero toView:tableView];
NSIndexPath *hitIndex = [tableView indexPathForRowAtPoint:hitPoint];
return hitIndex;
}
I used + (NSIndexPath *)indexPathWithTableView:(UITableView *)tableView sender:(id)sender
for buttons inside cells and it is correct, but for gesture, it always returns the cell of the last index path.
EDIT
Hard-coding the tap recognizer works
if ([message.type isEqualToString:#"image"]) {
UIImageView *view = ((ImageMessageCell *)cell).imageView;
UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(ui_tapped:)];
[view setGestureRecognizers:[NSArray arrayWithObject:tapRec]];
}
This is very tedious coding (since I need to check for each types of cells including image, video and other rich media), please post your answer if you know how to add it from the storyboard
It turns out that it's not able to add gesture recognizer to each instantiated prototype cell separately, i.e. only one gesture recognizer for all the cells instantiated from the same prototype cell.
Solved this problem by using UIButton with background image view instead of using image view.
[self.imageButton setBackgroundImage:[UIImage imageNamed:LOADING_IMAGE_FILE] forState:UIControlStateNormal];
NSData *blob = post.thumbnailBlob;
if (blob) {
[self.imageButton setBackgroundImage:[UIImage imageWithData:blob] forState:UIControlStateNormal];
}
In order for each cell to have it's unique tap gesture tag, it needs to be done in a proper sequence:
let tapGest = UITapGestureRecognizer(target: self, action: #selector(yourTapFunction(_:)))
//1 add gesture to the cell
cell.imageView.addGestureRecognizer(tapGest)
//2 assign a tag
tapGest.view?.tag = indexPath.row
#objc func yourTapFunction(_ tapGest: UITapGestureRecognizer) {
guard let index = tapGest.view?.tag else { return }
//do smth
}
if you reverse 1 and 2, the tag will always be 0
On a tableView that allows selection in edit mode, how do i find which was the selected cell when i call [tableView setEditing:NO animated:YES] and exit edit mode ?
There is no call to didDeselectRowAtIndexPath:, are there any other methods i overlooked ?
Edit for solution:
The cell never gets deselected, therefore indexPathForSelectedRow still returns the correct value. Fair enough.
you can use GestureRecognizer for this .
try this :
UISwipeGestureRecognizer *gestureObj = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(Method:)];
[gestureObj setDirection:UISwipeGestureRecognizerDirectionRight];
[self.tableview addGestureRecognizer:gestureObj];
-(void)Method:(UISwipeGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.tableview];
NSIndexPath *indexPath = [self.tableview indexPathForRowAtPoint:p];
if (indexPath == nil)
{
[self setEditing:YES animated:YES];
NSLog(#"slide on table view but not on a row");
}
else
{
[self setEditing:YES animated:YES];
NSLog(#"slide on table view at row %d", indexPath.row);
}
UITableViewCell *cell = [self.MYArray objectAtIndex:indexPath.row];
}
The answer is that the row never gets deselected.
indexPathForSelectedRow is still valid before actually exiting the edit mode.
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...!
if I want that the TableView goes on Top, I have used a Button with this Code:
[self.tableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
But its not really cool to use a Button to do this Action.
Is there a better way to do this? Should the User tapping 2x and then goes to the Top?
What is the best way ;)
Try looking at scrollToRowAtIndexPath:atScrollPosition:animated
It's probably a better idea than scrolling with scrollRectToVisible.
Thanks for the Replies, i have solved the Problem within the Rows. If a User tapping longer on a Row, he will be pushed to Top.
Call that in your Function
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Action -> long pressing on a Cell
UILongPressGestureRecognizer *longPressGesture =
[[[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(longPress:)] autorelease];
[cell addGestureRecognizer:longPressGesture];
}
Add this Function to your Class
//Method
- (void)longPress:(UILongPressGestureRecognizer *)gesture
{
// only when gesture was recognized, not when ended
if (gesture.state == UIGestureRecognizerStateBegan)
{
// get affected cell
UITableViewCell *cell = (UITableViewCell *)[gesture view];
// get indexPath of cell
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
// do something with this action
NSLog(#"Long-pressed cell at row %#", indexPath);
//go to the first Row
[self.tableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
}
}
I hope that helps anyone.