UILabel with Gesture Recognizer inside UITableViewCell blocks didSelectRowAtIndexPath - ios

I use some UILabels with a UITapGestureRecognizer inside a UITableViewCell. The GestureRecognizer works well. But when I tap on the label, I want that the didSelectRowAtIndexPath: should execute too. Or even just the indexPathForSelectedRow() method should give me the selected row.
Setting cancelsTouchesInView = false did not work!
Is this possible? Right now the indexPathForSelectedRow() method returns nil.
Thanks

Why are you using UITapGestureRecognizer? If you want to use that, try to set the tag of label as label.tag=indexpath.row. So you might get the value you are looking at. Regarding my own opinion, I'd remove the uitapgesturerecognizer and directly use didselectrowatindexpath method..
EDIT 2:
Try using this solution..it might help you..
-(void)handleTap:(UITapGestureRecognizer *)sender
{
CGPoint location = [sender locationInView:self.view];
if (CGRectContainsPoint([self.view convertRect:self.yourTableView.frame fromView:self.tableView.superview], location))
{
CGPoint locationInTableview = [self.yourTableView convertPoint:location fromView:self.view];
NSIndexPath *indexPath = [self.yourTableView indexPathForRowAtPoint:locationInTableview];
if (indexPath)
[self tableView:self.yourTableView didSelectRowAtIndexPath:indexPath];
return;
}
}

Related

didSelectItemAtIndexPath of UICollectionView not getting called when its in uiscrollview

I have taken the UIScrollView inside that
i have taken one UIView with fixed
position and just below it taken UICollectionView which is horizontal scrolling,
then i have again UIView and then again i have taken
UICollectionView with fixed cell 1.
So, on select of item of both collection view's didSelectItemAtIndexPath method not getting called.I have found some solution here but not found exact one.
By using above solution, i am facing problem is on tap anywhere(ex. image gallary) tap of UIScrollview call the tap method but every time didSelectItemAtIndexPath called wheather i click on collection view or not default zero indexpath is called.
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(gestureAction:)];
[recognizer setNumberOfTapsRequired:1];
self.scrollViu.userInteractionEnabled = YES;
[self.scrollViu addGestureRecognizer:recognizer];
-(void)gestureAction:(UITapGestureRecognizer *) sender
{
CGPoint touchLocation = [sender locationOfTouch:0 inView:self.YourCollectionViewName];
NSIndexPath *indexPath = [self.YourCollectionViewName indexPathForRowAtPoint:touchLocation];
NSLog(#"%d", indexPath.item);
}

Change values of UITableViewCell on tap

I need to change the value inside a UITableViewCell when the user taps on it.
I need to modify the value trough an animation of a value inside a UITableViewCell.
Right now, I've implemented a UITapGestureRecognizer when the user taps on the UILabel, like so:
UITapGestureRecognizer *tapOnAmount = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnBalance)];
[cell.amountLabel setUserInteractionEnabled:YES];
[cell.amountLabel addGestureRecognizer:tapOnAmount];
Changing the values in a method didTapOnBalance will crash the app, like so:
-(void)tapOnBalance{
NSString *headerIdentifier = #"HeaderCell";
HeaderTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:headerIdentifier];
cell.amountLabel.text = #"new Value"; // this will crash because at runtime
// the compiler won't recognize cell.amountLabel...
}
Implementing this in the UITableViewCell will cause me to send the values of the HeaderTableViewCell to the subclass and I don't know how to do that either.
You can't just deque a new cell, that will not give you the cell that the user tapped - it will make a new one. But, if you change your tap handler just a little, you can get the index path of the cell tapped from the gesture.
You need a slight change to the initialization of the gesture (look at the selector):
UITapGestureRecognizer *tapOnAmount = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapOnBalance:)];
[cell.amountLabel setUserInteractionEnabled:YES];
[cell.amountLabel addGestureRecognizer:tapOnAmount];
and then another slight change to your handler:
- (void)tapOnBalance:(UITapGestureRecognizer *)sender
{
CGPoint location = [sender locationInView:self.view];
CGPoint locationInTableview = [self.tableView convertPoint:location fromView:self.view];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:locationInTableview];
// then you can either use the index path to call something like configureCell or send a didSelectRowAtIndexPath like this:
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}
Your code is totally wrong.
You're creating a new cell when the user taps on an existing cell, and trying to change the value displayed in that new cell. Don't do that.
Instead, change the data in your table view's data model, then tell your table view to reload that cell (as explained in ZAZ's answer.) If you've changed the data model to reflect new info for your cell, reloading it will cause it to be displayed with the new settings.
you must implement didSelecRowAtIndexPath and write in it the following line of code after the changing the value to animate the tapped row
[self.myTableView reloadRowsAtIndexPaths:indexPath] withRowAnimation:UITableViewRowAnimationNone];
Hope it helps!

How to add tap gesture to UICollectionViewCell subview returned from dequeueReusableCellWithReuseIdentifier

What's the best method for efficiently adding a tap gesture to a subview of a UICollectionViewCell returned from dequeueReusableCellWithReuseIdentifier that already has a bunch of default gesture recognizers attached to it (such as a UIScrollView). Do I need to check and see if my one custom gesture is already attached (scrollView.gestureRecognizers) and if not then add it? I need my app's scrolling to be as smooth as possible so performance of the check and efficient reuse of already created resources is key. This code all takes place inside cellForItemAtIndexPath. Thanks.
I figured out a way to do it that requires only a single, shared, tap gesture recognizer object and moves the setup code from cellForItemAtIndexPath (which gets called very frequently as a user scrolls) to viewDidLoad (which gets called once when the view is loaded). Here's the code:
- (void)myCollectionViewWasTapped:(UITapGestureRecognizer *)tap
{
CGPoint tapLocation = [tap locationInView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:tapLocation];
if (indexPath)
{
MyCollectionViewCell *cell = (MyCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
CGRect mySubviewRectInCollectionViewCoorSys = [self.collectionView convertRect:cell.mySubview.frame fromView:cell];
if (CGRectContainsPoint(mySubviewRectInCollectionViewCoorSys, tapLocation))
{
// Yay! My subview was tapped!
}
}
}
- (void)viewDidLoad
{
// Invoke super
[super viewDidLoad];
// Add tap handler to collection view
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(myCollectionViewWasTapped:)];
[self.collectionView addGestureRecognizer:tap];
}
Here's a rough, very simple outline of a possible design solution: you could subclass UICollectionViewCell and override its initialization methods to add the gesture recognizer to its subviews. Furthermore, if you don't want the cell to "know" about the gesture recognizer, you could create a protocol that the data source object would conform to. The cell object would call a "setup" protocol method at the appropriate time.
Hope this helps!

indexPathForRowAtPoint returns nil only for first cell in a uitableview

I have a UIButton inside of a custom UITableViewCell that removes the cell when the button is pressed. In order to get the indexPath of the cell the button is in, I'm calling indexPathForRowAtPoint: in a method called when the button is pressed as follows:
-(void)buttonPressed:(UIButton *)sender{
CGPoint btnPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:btnPosition];
if (indexPath != nil)
{
//remove the cell from the tableview.
}
}
This works fine if I press the button in any cell except the first cell, and also when if there is ONLY one cell in the table. However, if there are multiple cells and I press the button in the first cell, the button press is triggered and the btnPosition is found, but [self.tableView indexPathForRowAtPoint:btnPosition]; returns nil.
For example, if there are three cells, each with a button:
I press the first cell button: buttonPressed is called but indexPath is nil, so nothing happens.
I press the third cell button: buttonPressed is called and the cell is removed as expected.
I press the first cell again: same as before, buttonPressed called but indexPath is nil.
I press the second cell button: buttonPressed is called and the cell is removed as expected.
I press the first cell button: This time, buttonPressed is called, indexPath is not nil and the cell is removed as expected.
I've checked that the tableView is not nil, as suggested by a related, but different, post. I've also confirmed that the point btnPosition is set to the same value (x=260, y=44.009995) both when [self.tableView indexPathForRowAtPoint:btnPosition] == nil AND when [self.tableView indexPathForRowAtPoint:btnPosition] != nil.
So aside from asking if anyone has any ideas on what could be happening here, my question is:
Under what circumstances could passing the same CGPoint to [self.tableView indexPathForRowAtPoint:btnPosition] return a different (or nil) indexPath?
I'd appreciate help with any ideas of where I might look to track this down, or to hear if anyone has encountered a similar issue.
Some additional notes that might be helpful (please excuse if I make a question asking faux-pas. I'll happily take your feedback about that as well :)
This tableView is part of a UITableViewController which is embedded in a UINavigationController
The tableView has 3 sections, 2 of which are hidden from view (0 rows, no footer, no header) while I'm presenting the section with the button cell rows as described.
I'm adjusting the location of the buttons during presentation by programmatically changing their horizontal constraints.
The heights of my customUITableViewCell, tableViewRows and UIButton are each equal to 60.0f
Just try this:
fix target selector:
[button addTarget:self action:#selector(buttonPressed:event:)forControlEvents:UIControlEventTouchUpInside];
- (void)buttonPressed:(id)sender event:(id)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPos = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:touchPos];
if(indexPath != nil){
//do operation with indexPath
}
}
I feel awful that I will get points for this, but #rmaddy's comment worked wonders for my code. I want to put this as an answer since I missed his comment the first time round and spent ages implementing a longer fix. Thanks rmaddy for the fix:
As an experiment, try using CGPointMake(5,5) instead of CGPointZero.
Does that make any difference?
rmaddy then explained why it worked:
Given that you were using CGRectZero and the fact that the y
coordinate of btnPosition was a strange value (44.009995 instead of
44), I suspected you may have either had an edge case or been victim
to roundoff error. By choosing a point that wasn't in the absolute
corner of the view, you had a better chance of avoiding roundoff
issues.
rmaddy - this issue has been bugging me for 2 weeks... and you fixed it. Thank you very much. Can I donate any points on stackoverflow to you?
I am going to share the way I do this type of functionality. This will work if you have one section. You don't need these 2 lines:
CGPoint btnPosition = [sender convertPoint:CGPointZero toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:btnPosition];
In your cellForRowAtIndexPath while adding a custom cell add these line:
cell.btn.tag = indexPath.row;
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
And in buttonPressed do this:
-(void)buttonPressed:(UIButton *)sender{
UIButton *btn = (UIButton *)sender;
//Remove the btn.tag number row.
}
For more than 1 section you can do this:
-(void)buttonPressed:(UIButton *)sender{
UIButton *btn = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)btn.superview;
UITableView *tableView = (UITableView *)cell.superview;
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
//Remove this cell in indePath
}
Hope this helps .. :)
Add UITapGestureRecognizer to UIButton as well as add a target Method for GestureObject.
UITapGestureRecognizer *tapGestureDelete=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(callAMethod:)];
[yourDeleteButton addGestureRecognizer:tapGestureDelete];
In taget callAMethod retrieve the gestured position as below
-(void)callAMethod:(UIGestureRecognizer *)gestureRecognizer
{
NSIndexPath *currentIndexPath = [self.tableView indexPathForRowAtPoint:[gestureRecognizer locationInView:self.talbleView]];
if(currentIndexPath != nil)
{
// remove the cell from tableview
}
}
I Hope It will work...
simalone's answer in Swift (as of version 2.2) will be:
tableViewCell.button.addTarget(self, action: #selector(buttonPressed(_:event:)), forControlEvents: .TouchUpInside)
...
#IBAction func buttonPressed(sender: UIButton, event: UIEvent) {
if let touchPos = event.allTouches()?.first?.locationInView(self.TableView),
let indexPath = self.TableView.indexPathForRowAtPoint(touchPos)?.row {
// do operations with indexPath
}
}

How to pass gestures from UITextView to UICollectionViewCell

I have a horizontal scrolling UICollectionView with UICollectionViewCells that contain a UITextView. Is there any way to pass gestures on the textview to the cells, so that didSelectItemAtIndexPath gets called?.
I tried it with subclassing UITextView and passing touchesbegin/end to the cell, but that didn't worked.
You can make the view non-interactive, which will cause touches to get passed through:
textView.userInteractionEnabled = NO;
If you need it to be interactive, you can try this:
textView.editable = NO;
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped)];
[textView addGestureRecognizer:tap];
... and then add this function to your UICollectionViewCell subclass:
-(void) tapped {
UICollectionView *collectionView = (UICollectionView*)self.superview;
NSIndexPath *indexPath = [collectionView indexPathForCell:self];
[collectionView.delegate collectionView:collectionView didSelectItemAtIndexPath:indexPath];
}
I haven't tested it though...
Well, if your cell is the superview of the text view, you could implement something like this in the UITextViewDelegate method textViewDidBeginEditing:.
- (void)textViewDidBeginEditing:(UITextView *)textView {
NSIndexPath *indexPath = [self.collectionView indexPathForCell:(UICollectionViewCell *)textView.superview];
[self.collectionView selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionTop];
}
This doesn't seem to work in iOS6.x: the all view in a UICollectionViewCell seem to be embedded in a UIView that is the first child of the cell. In order to get the actual cell that is the UITextView is in you will need to dereference a second time. In other words the order is (from bottom to top):
UITextView->enclosingUIView->UICollectionViewCell

Resources