UITableView didSelectRowatIndexPath not called on single tap - ios

Very very strange! It works everywhere but here:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MyViewController* cliente = [[MyViewController alloc] initWithModeAndClientId:2 c:cid];
cliente.delegate = self;
UINavigationController *n = [[UINavigationController alloc] initWithRootViewController:cliente];
n.navigationBarHidden = NO;
[[n navigationBar] setBarStyle:UIBarStyleBlack];
[self presentViewController:n animated:YES completion:nil];
}
If I tap by a single click on the row, the MyViewController shows after seconds!
If I click twice, it shows rapidly!
In the Profiler, at single click nothing happens...
I have no didDeselectRowAtIndexPath method.

The solution is to put on the main thread the loading of the second controller
dispatch_async(dispatch_get_main_queue(), ^{
// Code here
});

Its issue of threading, when you tap a row in tableview it start a new thread so the presenting a view may take longer to show up on screen.
The solution is:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
dispatch_async(dispatch_get_main_queue(), ^{
MyViewController* cliente = [[MyViewController alloc] initWithModeAndClientId:2 c:cid];
cliente.delegate = self;
UINavigationController *n = [[UINavigationController alloc] initWithRootViewController:cliente];
n.navigationBarHidden = NO;
[[n navigationBar] setBarStyle:UIBarStyleBlack];
[self presentViewController:n animated:YES completion:nil];
});
}

Are you by any chance putting a tableview inside a scroll view? If so the container scrollview is blocking the touch event to the inner table view. This fixed it for me:
self.myContainerScrollview.panGestureRecognizer.delaysTouchesBegan = true
Credit for the answer should go to the OP here: https://stackoverflow.com/a/31040918/1455770

Had the same problem. And it was hard to find. But it was because of following:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
return nil;
}
You should return indexPath

I had the same issue on my tableView(swift 4.2). After debugging and taking lots of time it appeared that table view has a boolean property named allowsMultipleSelection.
If allowsMultipleSelection is set to true the table view selection mechanism will change in a way that by selecting each cell the tableView didSelectRowAtIndexPath: is called for the first time and by selecting the same cell for the second time the tableView didDeselectRowAtIndexPath: is called.
This makes tableView didSelectRowAtIndexPath: function to be called on the third selection for the same cell and so the result is double tap for calling didSelectRowAtIndexPath:.
It means that if the number of times a cell tapped are odd (1, 3, 5, ...) then always tableView didSelectRowAtIndexPath: will be called and if the number of times a cell tapped are even (2, 4, 6, ...) then always tableView didDeselectRowAtIndexPath: will be called.
If you want the tableView didSelectRowAtIndexPath: to be called on each selection for a cell then the tableView multiple selection has to be set false, tableView.allowsMultipleSelection = false.
By doing this, every time the cell is tapped tableView didSelectRowAtIndexPath: will be called on table view and by selecting another cell tableView didDeselectRowAtIndexPath: will be called for the cell was selected before and then the tableView didSelectRowAtIndexPath: will be called for the newly selected cell.
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsMultipleSelection = false
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("This will be called for each cell tap")
}

Related

UITableview doesn't call cellForRowAtIndexPath

The first time the view controller is pushed (from the previous view controller) all the delegate methods are called (inside a navigation controller).
When pressing back to return to the previous view controller , and then pushing it again (for the second time)
cellForRowAtIndexPath isn't called but numberOfRowsInSection and numberOfSectionsInTableView are called.
The reloadData is called within
-(void)viewWillAppear:(BOOL)animated
{
[self.tableView reloadData];
}
and I have tried in
-(void)viewDidAppear:(BOOL)animated
{
[self.tableView reloadData];
}
and it doesn't help.
Edit
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1; // called
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3; // called
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// relevant code for cell - THIS METHOD IS NOT CALLED SECOND TIME
}
If you don't find any valid reason as explained above I will highlight
another mistake one can make is (as I once did).
The steps to note are
1) initialise your custom table view as first step
2) set the delegate and datasource before you add the tableView to you view.
If one has done these steps mistakenly after adding to the tableview to your self.view object, then it would be a silent way to make yourself struggle.Correct order is as under:
self.myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, myViewwidth, 120) style:UITableViewStylePlain];
self.myTableView.tag = MY_TABLEVIEW_TAG;
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
[self.view addSubview:self.myTableView];
This always happens when you define your underlying data source (array or Dictionary) as weak, the first time it gets pushed, the data is there, when deallocated, it will release and you lose control over it.
Double check the weak/strong condition and optionally set the data source before pushing again.
Please check if you have set/connected Delegate and Datasource to the File's Owner.
And check the array count of your model, if it contains value of not?
NSLog in the numberOfRowsInSection method and check it by using breakpoints and step over.

IOS UITableViewCell De-select a view once selected and returned to page

How can I deselect a cell when returning to a view?
I have an orange down state which is applied to a cell when selected - the cell navigates to a modal view when clicked - when i click back button the cell is still selected.
I have tried applying this code -
[tableView deselectRowAtIndexPath:indexPath animated:YES];
to my cellForRowAtIndexPath method - but it doesn't do anything!
Update - Having done a bit of research - It appears Ive missed some valuable information out of this question! - my table view is a UITableView embedded in a View Controller - not a UITableViewController - so it sounds like it doesnt have the available methods which are required for the suggestions so far..
You could use UITableViewController's clearsSelectionOnViewWillAppear property.
You should not call deselectRowAtIndexPath in cellForRowAtIndexPath method.
you can do this in viewWillAppear
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear: animated];
NSIndexPath *selectedIndexPath = [tableViewObj indexPathForSelectedRow];
if (selectedIndexPath != nil) {
[tableViewObj deselectRowAtIndexPath:selectedIndexPath animated:YES];
}
}
Or you can write in didSelectRowAtIndexPath as well
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath: indexPath animated:YES];
}
This is the right approach
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath: indexPath animated:NO]; // first line in this method
// rest of code
}

iOS: Storyboard CollectionView segue not being triggered

I have a UICollectionView controller embedded inside a navigation controller. The collectionView lists projects and each cell is supposed to segue to a ProjectDetail screen.
I simply cannot get the segue to trigger. If I simply drop a button on the nav bar and hook up a segue to the detail, it works. But triggering from my CollectionView cell doesn't.
Here is what the storyboard looks like: http://cl.ly/RfcM I do have a segue hooked up from the CollectionViewCell to the ProjectDetailViewController
Here's the relevant code inside my ProjectDetailViewController:
#interface ProjectCollectionViewController () {
NSArray *feedPhotos;
Projects *projects;
}
#end
#implementation ProjectCollectionViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[FeedViewCell class] forCellWithReuseIdentifier:#"cell"];
[self loadData];
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"selected %d", indexPath.row);
Project *project = [projects getProject:indexPath.row];
NSLog(#"project = %#", project);
}
- (void)loadData {
[self.projectLoader loadFeed:self.username
onSuccess:^(Projects *loadedProjects) {
NSLog(#"view did load on success : projects %#", loadedProjects);
projects = loadedProjects;
[self.collectionView reloadData];
}
onFailure:^(NSError *error) {
[self handleConnectionError:error];
}];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return projects.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"cell";
FeedViewCell *cell = (FeedViewCell *) [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:1.0];
UIImageView *cellImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
Project *project = [projects getProject:indexPath.row];
NSString *imageUrl = [project coverPhotoUrl:200 forHeight:200];
NSLog(#"imageurl =>%#", imageUrl);
if (imageUrl) {
[cellImageView setImageWithURL:[NSURL URLWithString:imageUrl]];
}
[cell addSubview:cellImageView];
cell.imageView = cellImageView;
return cell;
}
I'm guessing the problem is somewhere in how I'm hooking up the Cells to the CollectionView.
Any help would be greatly appreciated!
You cannot create segues directly from cells in a storyboard because the collectionview is populated dynamically through the data source. You should use the collectionView:didSelectItemAtIndexPath: and perform the segue programatically using performSegueWithIdentifier:sender:. Something like this:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"MySegueIdentifier" sender:self];
}
where MySegueIdentifier is the identifier of the segue defined in storyboard.
TLDR: FOR A STORYBOARD, do not call registerClass:forCellWithReuseIdentifier:. It overrides what the storyboard sets up for the cell (including how segues are handled):
How to set a UILabel in UICollectionViewCell
Brief setup
Used a storyboard
Created a new collection view controller using the Xcode template,
setting it as a subclass of UICollectionViewController.
Initially used the default UICollectionViewCell, adding a UILabel
programmatically.
The generated UICollectionViewController code registered the cell in viewDidLoad:
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
First Issue:
The prepareForSegue:sender: event was not firing, which brought me to this answer .
I implemented the UICollectionViewDelegate and collectionView:didSelectItemAtIndexPath: event, then called the segue programmatically.
This fixed my first issue.
Second Issue: I switched to a custom cell containing one label. After hooking everything up, the cell label was not displaying.
After some digging, I found a solution contained in the link at the top of my answer.
Third Issue and Solution: I removed the registerClass:forCellWithReuseIdentifier: line. When I ran my app, the label appeared correctly, but when I tapped a cell, it called the prepareForSegue:sender event twice. By removing the registerClass:forCellWithReuseIdentifier line, the cell was processing cell touches directly, without the need of the delegate method. This is how I expected the storyboard to work. I deleted the collectionView:didSelectItemAtIndexPath: event, which resolved the double-firing of prepareForSegue:sender:. If you are using a storyboard, do not register the cell class. It overwrites what storyboard sets up.
Have you made your CollectionView Cell's connection in Triggered Segues on selection?
You can also trigger a segue programatically using
[self performSegueWithIdentifier:#"segueIdentifier" sender:nil]
in
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
Equivalent Swift code for similar question.
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier(#"TargetSegway", sender: self)
}
Make sure, in case if your cell has other overlapping views, "User Interaction Enabled" is unchecked (you can find this option, under attribute inspector View/Interaction). Otherwise, your Tap Gesture is consumed by the overlapping view, didSelectItemAtIndexPath may not be called.

iphone - didSelectRowAtIndexPath: only being called after long press on custom cell

I am creating one table view based application. I have created a custom table cell for table, that contains 2 labels, 1 image and 1 button. The table view Data source method is working properly. I am using xib for both custom cell and view controller class and i connect delegate and data source to the file's owner. But the problem is when i select the table row, didSelectRowAtIndexPath is not getting fire. As mentioned the only way to fire it is to hold down on the cell for about 3-4 seconds. Does anyone have any idea why this is happening?
Thanks for any pointers...
Here is my table view methods..
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [finalAddonsArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
NewCustomCell *cell = (NewCustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib=[[NSBundle mainBundle]loadNibNamed:#"NewCustomCell" owner:self options:nil];
cell=[nib objectAtIndex:0];
}
Addons *addons1=[[Addons alloc]init];
addons1= [finalAddonsArray objectAtIndex:indexPath.row];
if (addons1.data == nil) {
cell.ivCategory.image = [UIImage imageNamed:#"blogo.jpg"];
}
else
{
cell.ivCategory.image=[UIImage imageWithData:addons1.data];
}
cell.lblTitle.text = addons1.name;
if (addons1.price == nil) {
cell.lblPrice.text = nil;
}
else{
cell.lblPrice.text = [NSString stringWithFormat:#"%# rs",addons1.price];
}
[cell.button addTarget:self
action:#selector(editButtonPressed:)
forControlEvents:UIControlEventTouchUpInside];
cell.button.tag=indexPath.row;
index = indexPath;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"sjcjksbcjksbcfkebscf1234567890");
}
One more thing i am getting that if i am using default UITableViewCell instead of custom cell then also my problem is same, delegate method is not getting fire.
Custom cell properties:
same problem happened with me because I have added a tap gesture recogniser over it.
If you have used any gesture recognizer try removing it and check if it causing the problem.
EDIT: Solution as commented by the Ali:
If you have used tap gesture you can use [tap setCancelsTouchesInView:NO];
I was faced with a similar issue:
For me, the problem was because my UITableView was added to an UIScrollView and more specifically to its contentView.
It appears that inside the contentView, I had to stay press 2-3 sec to fire the didSelectRowAtIndexPath method.
I moved my TableView to self.view instead of contentView and it solved the problem!
Maybe you will call the method
[tableView deselectRowAtIndexPath:indexPath animated:NO];
before Push ViewController or Other Operation. Like
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// 1. manual call this method to deSelect Other Cell
[tableView deselectRowAtIndexPath:indexPath animated:NO];
// 2. than do other operation
PushViewController Or Some Animation ....
}
that`s solve my problem .
As others suggested, [tap setCancelsTouchesInView:NO]; does the trick.
However, I want to make one thing clear:
If you think that you did not implement tapgesture and are curious about why you had to add your view into the protected views, check out your class because most probably you have inherited some class and that class includes tap gesture recognizer in it.
In my case, I did the following:
- (NSMutableArray *)tapProtectedViews
{
NSMutableArray *views = [super tapProtectedViews];
[views addObject:self.mTableView];
return views;
}
Edit for Swift 4+
Assuming you have a UITapGestureRecognizer instance named tapGesture:
func disableTapGesture(){
tapGesture.cancelsTouchesInView = false
}
Or you can:
if self.view.gestureRecognizers?.isEmpty == false{
for recognizer in self.view.gestureRecognizers!{
self.view.removeGestureRecognizer(recognizer)
}
}
Dear i faced the same problem. When i tapped the cell but didselectrowatindexpath was not called than it was suddenly called when i released the button after pressing it for few seconds.
If you are facing the same issue there must be a
1. UITapGestureRecognizer that is creating problem for you
or
2. a scroll view in which you placed you table view.
Thus you should remove the gesture or the super scroll view in which your table view is placed
If you have custom gesture object on your view, check override func gestureRecognizerShouldBegin(_ gesture: UIGestureRecognizer) -> Bool delegate. Compare custom gesture with sender gesture, If its not custom gesture object, pass it to the the super. So system gestures/taps won't get blocked.
I'm not sure about this, but Delays Content Touches might have something to do with it.

UITableView didSelectRowAtIndexPath not being called on first tap

I'm having an issue with UITableView's didSelectRowAtIndexPath of ios 5, which is correct in ios 4.
The first time I tap any row in the table, the method does not get called. Once I select another row, it call the didSelectRowAtIndexPath, but pass the last indexPath.
I've set tableView.delegate already, and it can run correctly in ios4.
MainView.xib
UITabBarController
--UINavigationController
--**UITableViewController**
Any suggestions?
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [[NSString alloc]initWithFormat:#"%d",indexPath.row];
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%d",indexPath.row);
}
You may have implemented (notice the Deselect): didDeselectRowAtIndexPath
...hence the trigger upon selecting a new cell which deselects the first one, and logging indexPath as the first one as well.
SOLUTION: didSelectRowAtIndexPath
yeah, they look super similar, and it doesn't help that didDeselectRowAtIndexPath is the first method Xcode's autocomplete selects most of the time.
FULL CODE:
// right
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {}
Please, try to change your -viewDidLoad to the code below (insert last line). Tell me about the results.
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
[self.tableView reloadData];
}
I am not sure if this has been answered but I was running with this issue and just like "Anna Karenina" commented above. Make sure that you pay close attention to detail.
This is cause by you implementing the DEselect
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
This will cause the first click not to work but following clicks and any other cell will work as expected.
Solution: make sure you pay attention and use didSelectRowAtIndexPath
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
hope that helps and like I mention before this was solved by "Anna Karenina"
Please Connect Delegate from Xib to Files Owners. Then Try it Will Work.
Add this code in your viewDidLoad method
[self.tableView reloadData]
I had a UIGestureRecognizer on the view inside of the storyboard. I had to remove it and it worked like normal.
I also had this problem when passing data to another VC. I solved it by switching to a manual segue from the Table VC to the detail VC.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedData = data[indexPath.row]
performSegue(withIdentifier: "Detail", sender: self)
}

Resources