How to understand which UITableViewCell has been selected - ios

I have a list of NSStrings stored in an NSMutableArray, each value in the NSMutableArray has been assigned to a UITableViewCell. Using the following method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
How do I figure out which cell was selected. I'm looking for some kind of value through NSIndexPath or any other method to change the icon of a UITableViewCell when it is selected. After I know which cell has been selected, I'd reassign the cell to the new image, and then reload the data using: [self.homeworkTable reloadData].
I'm looking for some kind of simple return value, like 0 if the first cell is selected, etc. I've tried utilizing the NSIndexPath with no luck, as an NSIndexPath is not an NSString. How would I go about doing this?
EDIT
I understand the answer may already be on Stack Overflow, I simply haven't found it, and redirecting me to the answer would be very much appreciated.

You can get it directly didSelectRowAtIndexPath (that's the whole point!) using indexPath.row
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
int index = indexPath.row;
//....
It will match the index of your datasource array.
Sometimes you will need to get it from outside didSelectRowAtIndexPath deleguate method, you can do it the following way:
NSIndexPath *indexPath = [self.homeworkTable indexPathForSelectedRow];
int index = indexPath.row;

UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

You should use indexPath.row, this gets you the row that you selected in this tableview.
You should also check out the documentation on NSIndexPath:
https://developer.apple.com/library/Mac/documentation/Cocoa/Reference/Foundation/Classes/NSIndexPath_Class/Reference/Reference.html

Related

Is cellForRowAtIndexPath called when you use a custom cell?

I am using a custom cell class in a tableview controller.
When I include a statement in the tableviewcontroller in cellForRowAtIndexPath NSLog(#"method called"): it does not seem to get called.
Is it possible that this method is not called when you have a custom cell?
Edit:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"cell for row at index path called");
NSDictionary *item= [self.getItems objectAtIndex:indexPath.row];
//This sets place in storyboard VC
IDTVCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.item = item;
if (cell == nil) {
cell = [[IDTVCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"Cell"];
}
return cell;
}
cellForRowAtIndexPath is not called if no rows are returned.
-tableView:cellForRowAtIndexPath: is not getting called
That is what happened in my case.
It can also not get returned if you reload table on wrong thread and in certain other scenarios.
cellForRowAtIndexPath: not called
However, a custom cell per se does not cause this..
To answer your question - Yes, it is.
There could be n-number of reasons why cellForRowAtIndexPath: is not getting called. This may be because delegate / dataSource is not set or UITableView frame is not set... etc. etc.
You should easily find a solution with more online research and closure look at your code.

how to use the function dequeueReusableCellWithIdentifier:forIndexPath?

I know that dequeueReusableCellWithIdentifier:forIndexPath is called by tableView method inside the tableviewcontroller. And if I understand correctly, tableView method is called several times until all the cells are populated. But what I don't know is where do you get the value for the parameter IndexPath? I want to use dequeueReusableCellWithIdentifier:forIndexPath for a method that I created because I want to access my cell to copy some values of its properties.
NOTE:
I already populated my cell, which means that I successfully used the method tableView.
(Edit) ADDITIONAL INFO:
I'm trying to create a profile and edit profile tableviews. Inside the profile tableview, I displayed the name, address, contact#, etc., of the user. Also, I have a segue called edit profile. In the edit profile, I have textfields for each category (name, address, etc.). What I want to do is, if I edit the contents of the textfields, I should be able to display the new contents in my profile tableview. An example case would be: in the profile view I'm displaying -> name:human, address:earth (each in its own cell). Now if I go to editprofile tableview, I will edit the contents such that -> name:alien, address:mars. After that, there is a button called 'apply' to end editing of contents and go back to profile tableview. If I go back to profile view, the display should now be name:alien, address:mars and not name:human, address:earth.
Here is some code if it's any help. The code is called by a button in tableviewcontroller. "MyCell" is the class of my cell. This code is not working properly. I hope someone can help me fix this.
- (IBAction)updateCopies:(id)sender {
static NSString *ident = #"MyCell";
NSIndexPath *indexPath;
//create cell
MyCell *cell = [self.tableView dequeueReusableCellWithIdentifier:ident forIndexPath:indexPath];
//create variable for accessing cells
int row = [indexPath row];
_labelValues[row] = cell.textField.text
}
You should only use dequeueReusableCellWithIdentifier when you need to supply the table view with a cell to display. If you want to get the UITableViewCell object at a certain index, you should use cellForRowAtIndexPath.
Your problem
What you really need is a model class. You can then pass this to the edit controller, which changes the properties. Then when you return to the tableView, you can reload it and display the new properties.
What you could also do is create a delegate protocol for your edit profile controller, something like EditProfileViewControllerDelegate with something like:
protocol EditProfileViewControllerDelegate {
- (void)editProfileViewController:(EditProfileViewController *)controller didUpdateName:(NSString *)name address:(NSString *)address;
}
You can implement this delegate in your table view controller and use it to update the values when the text is changed. However, this quickly becomes unwieldy, I would not recommend it over using a proper model class.
You can get indexPath using CGPoint..You can use dequeueResusableCell for reusability of the cell..
- (IBAction)updateCopies:(id)sender {
CGPoint position = [sender convertPoint:CGPointZero
toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:position];
//create variable for accessing cells
MyCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
int row = [indexPath row];
_labelValues[row] = cell.textField.text
}
Hope it helps you..
Use this
- (IBAction)updateCopies:(id)sender {
MyCell *parentCell = (MyCell *)sender.superview;
while (![parentCell isKindOfClass:[MyCell 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:(MyCell *)parentCell];
NSLog(#"indexPath = %#", indexPath);
}
Well I got what you want to accomplish.
Firstly, there is a delegate which is being called when you click/select a cell and go to the Edit Profile page. That delegate is
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
///
}
Make a global variable, say selectedIndexPath which holds the current cell index path which is being edited. Update this value each time when you go to edit profile page.
Like this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
selectedIndexPath = indexPath;
// code to go to edit page...
}
Now in your updateCopies Method, do like this
- (IBAction)updateCopies:(id)sender {
//get the existing cell with the indexPath
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[selectedIndexPath]];
[self.tableView endUpdates];
//rest of your code goes here...
}

Validity of the UITableView Cells in ViewWill Appear

What I have
1). Container has UITableView, which has two custom UITableViewCells.
2). Core Data has certain entity which has a text to be displayed at
UITableViewCell each time I get into the View.
What i am doing ?
1) I have chosen -viewWillAppear method which gets invoked each time the view is visible.
2) In -viewWillAppear, I retrieved the data from core data.
3) Retrieved particular cell from UITableView
NSUInteger idxArr[] ={2,0}; // 2 nd section, 0th Row.
NSIndexPath *cPath = [NSIndexPath indexPathWithIndexes:idxArr length:2];
myCell *tCell = (myCell *)[self.settings cellForRowAtIndexPath:cPath];
tCell.myLabel.text = rec.servername; // rec.servername is from DC.
When I checked in the lldb,
tCell was nil.
Questions:
1) It is the right way of getting the Cell ?
2) Or, By the time -viewWillAppear, does the UITableView not Ready ?
I am sure.
You should populate the cells by conforming to tableView dataSource protocol and then in your viewWillAppear you should call reloadData on your tableView.
After calling reloadData for tableview, We need to call -scrollToRowAtIndexPath: before getting cell from -cellForRowAtIndexPath:.
Because, As we are calling a row in section 2, it might not be in the visible area until we scroll. So, cellForRowAtIndexPath: returns nil.
Method -cellForRowAtIndexPath: shouldn't be called programically. It's a data source method for UITableView and it contain some cell reuse optimalizations. If you update the view after scrolling down and up -tableView:cellForRowAtIndexPath will be called again and your changes won't be visible.
If you want to update specific cell you should update make changes in:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
YourCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellId" forIndexPath:indexPath];
YourData *data = //Get your data here
if (data.isReady) {
cell.tf.text = data[indexPath.row].text;
} else {
cell.tf.text = #"Not ready yet. Need to reload this cell later";
}
return cell;
}
And then call method below when you finish fetch your data.
[self.tableView reloadRowsAtIndexPaths:(NSArray *) withRowAnimation:UITableViewRowAnimationFade];
If you want to reload whole tableView (usually it's not slow) as #salaman140 says you can call [self.tableView reloadData] to update all visible cells.
If I were you I wouldn't use:
NSUInteger idxArr[] ={2,0}; // 2 nd section, 0th Row.
NSIndexPath *cPath = [NSIndexPath indexPathWithIndexes:idxArr length:2];
I would (is much more clear):
NSIndexPath *cPath = [NSIndexPath indexPathForRow:0 inSection:2];

New solution to detect tableView reloadData completion?

I need to detect when a tableview has finished reloading data. There was an older solution where you could subclass the tableview then overload the reloadData method, however apparently that no longer works because tables are handled on multiple threads now and reloadData is called before cellForRowAtIndexPath.
My question is, has there been any solution to this problem since the change?
My problem is I am losing the pointer to a textField when the table reloads its data, so the first responder I am trying to set to the next text field (to auto focus on the next data input field), is lost.
This is essentially a repeat of #wain 's answer, but I thought I would add a little code.
You can keep a reference to the index path of the cell that owns the active text field (as a property).
Then, in cellForRowAtIndexPath: something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
MyTableViewCell *cell = (MyTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//I would hold a reference to the text field as a property on a subclass of UITableViewCell so that you can check for whether it exists.
if (!cell.textField) {
cell.textField = [[UITextField alloc] initWithFrame:cell.contentView.frame];
[cell.contentView addSubview:cell.textField];
}
return cell;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
UITableViewCell *cell = (UITableViewCell *)textField.superview.superview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
self.indexPathForActiveTextField = indexPath;
}
- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
MyTableViewCell *cell = (MyTableViewCell *)textField.superview.superview;
NSIndexPath *ip = [self.tableView indexPathForCell:cell];
[self.tableView reloadData];
NSIndexPath *nextIndexPath = [NSIndexPath indexPathForRow:ip.row+1 inSection:ip.section];
MyTableViewCell *theNewCell = (MyTableViewCell *)[self.tableView cellForRowAtIndexPath:nextIndexPath];
if (theNewCell) {
[theNewCell.textField becomeFirstResponder];
}
return YES;
}
Store the NSIndexPath of the table cell that holds the text field that should be the first responder. When you want to change the first responder you can ask the table view for the cell at that index path, then find the text field and make it first responder.
If the table view gets reloaded, in cellForRowAtIndexPath: check the index path and make the 'new' text field the first responder.
In this way you can set the first responder at any time and you can't loose the reference to it as the reference is to a location, not an object (which will be reused or removed).
UITableView uses a pool to reuse displayed cell. The target cell is possibly reused in other row. Storing the NSIndexPath like Wain suggested is good untill you not reorder the cells or delete some entry from the datasource. Define a key in the model, that you set the firstResponder according to that. Hope i did not misunderstood the problem.

UITableView and indexPathsForSelectedRows

I have a tableView that I have set allowsMultipleSelection to YES in storyboard.
EDIT
I was wrong about one thing... [tableView indexPathsForSelectedRows] does return a NSArray with 1 object in it during didSelectRowAtIndexPath.
However it does not work in cellForRowAtIndexPath after I reload the table so it will check which accessory (check mark or not) to apply.
In the original question I was trying to manually select the rows... Apparently that is handled by the tableView itself.. but somewhere along the way it is automatically deselecting my row as I never call deselectRowAtIndexPath on it...
Original Question:
For some reason when I set the cell to selected it does not change.
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//cell does not update selected property to YES after this next line
cell.selected = YES;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
I suppose I can keep track of the selected index paths myself... but I can swear I used a method that involved indexPathsForSelectedRows previously with success...
You cannot set the selected property directly
So instead of
cell.selected = YES;
Use
[tableView selectRowAtIndexPath:TheIndexPAth animated:YES scrollPosition:UITableViewScrollPositionBottom];
please post your all codes for this class in pastbean and put your link here we need more information,
and also try:
//in your interface
#interface YourClass (){
NSInteger _selectedIndex;
}
// cellForRowAtIndexPath: method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
// didSelectRowAtIndexPath: method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
_selectedIndex = indexPath.row;
[self.tableView reloadData];
}
Try [cell setSelected:YES animated:YES]
I'm pretty sure cell.selected is read only
A bit unfair since the question changed but.. for me.. the answer is either don't use
indexPathsForSelectedRows
or don't call
reloadTable
on your tableView.
I opted for the later, I decorated the accessory in didSelectRow and didDeselectRow instead of doing it on reload and just never reloading the table.
If someone can come up with a solution that involves both of the above, I will select that answer instead.

Resources