I'm calling a function in which i retrieve a NSDictionary with data that needs to be displayed in tableviewCells. Is it possible to call the method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
in another method?
It is not possible to call that one specific method inside another method. However, you can call [self.tableView reloadData] inside any methods. This call all the UITableView delegate methods which includes - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
You can explicitly call any methods of UITableViewDataSource and UITableViewDelegate, but should only be done in cases of dire need. In a project of mine, I get specific (subclassed) cells which draw elements not in the drawRect: method, but in a drawCell method I have defined, for performance gains (preventing offscreen rendering, etc).
Unless your cells have special drawing needs, (resizing subviews, etc) you should let the cellForRowAtIndexPath: method do this work for you.
Still, if you want to explicitly get cells, you may do with:
[yourTableView cellForRowAtIndexPath:indexPath];
This will return you your cell. Mind you, it will execute all relevant code inside the cellForRowAtIndexPath: including dequeueing, creation, and any other calls you have made in it.
indexPath passed here is an object of type NSIndexPath, and you may create one like this:
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:ROW_NUM inSection:SECTION_NUM];
Where, ROW_NUM is the is the row number inside the section, which is SECTION_NUM. For the first cell in the third section, the indexPath is:
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:0 inSection:2];//Since indices start at 0
Related
The master-detail project created by XCode wizard contains implementation of tableView:cellForRowAtIndexPath: that calls dequeueReusableCellWithIdentifier:forIndexPath:, then calls own custom method configureCell to fill the cell controls with valid data, and then returns the cell to caller. As I understand, this is how table knows how to draw its cells content.
What I don't understand is how to use it when I want to get cell from my code. And is it even supposed to be called by my code, or is it only a callback used by table framework itself?
For example, if I just want to change text of one label in cell, I thought I can just call tableView:cellForRowAtIndexPath:, and change what I need in the resulting cell object controls. But calling tableView:cellForRowAtIndexPath: actually makes a new cell object (probably reusing one from pool of unused cell objects) and fill ALL controls like imageviews and labels in it with data. This does not look good to me regarding to performance, when I want change just one label.
So maybe I could remove configureCell out of tableView:cellForRowAtIndexPath:? But then how to ensure all cell contents will be redrawn by system when [table reloadData] is called?
P.S.
XCode 7 wizard created this kind of code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];
[self configureCell:cell withObject:object];
return cell;
}
// Then later in controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] withObject:anObject];
break;
configureCell is called twice here. Seems like non-sense to me. I hoped at least people who write code for wizards understand how this is supposed to be working.
There's two different methods being called here that have similar names.
tableView:cellForRowAtIndexPath: is a UITableViewDataSource method that you implement to create the cell for the table to show. The tableview calls this method when a new cell is going to come on screen.
controller:didChange:... is calling a different method called cellForRowAtIndexPath: which is a UITableView method. This method queries the table for a cell that is already being displayed after previously being created using the tableView:cellForRowAtIndexPath: datasource method and doesn't result in it being called again. It's calling this method to access the already displayed cell and update it with new info when the object changes.
Both calls to configureCell are necessary.
In my project I'm creating custom cells by subclassing UITableViewCell. When cellForRowAtIndexPath: is fired I do a pretty basic stuff like:
MyCustomCell *cell = [self.tableView dequeueReusableCellWithIdentifier:[MyCustomCell identifier]];
I don't want to manually configure cell properties in cellForRowAtIndexPath: so I thought I'd create a method inside MyCustomCell called configureWithModel: which is filling MyCustomCell with proper data. So far, so good! Now inside cellForRowAtIndexPath: I have something like:
MyCustomCell *cell = [self.tableView dequeueReusableCellWithIdentifier:[MyCustomCell identifier]];
[cell configureWithModel:model];
In configureWithModel: I assign some data (image also) to cell so as you'd guess it could be slow'n'heavy so I wonder if this is a good solution to have a method like this in subclass of MyCustomCell? What is more, how it's related to prepareForReuse?
Doing this [cell configureWithModel:model]; is the best approach because take for a case when you want to use configureWithModel: in more than 2 tableViews you can avoid code redundancy and cell level control would be there with cell itself.
Use of [cell configureWithModel:model]; will make your code look like more structured, but for image use the following delegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
Example :
- (void)tableView:(UITableView *)tableView willDisplayCell:(AlbumCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
AlbumBO *album = [self.arrAlbums objectAtIndex:indexPath.row];
dispatch_async(imageQueue_, ^{
UIImage *image = [self retrieveImageWithImageUrl:album.coverPhoto];
dispatch_async(dispatch_get_main_queue(), ^{
[cell.imgVwAlbum setImage:image];
});
});
}
Here
AlbumCell is my Custom table cell
AlbumBO is the object for containing image object
And
[self retrieveImageWithImageUrl:album.coverPhoto]
is the user defined method to download image.
This sounds like a fairly decent usage of the singular responsibility principle. Where this might bite you is if your cells need to be binded with images that must be downloaded from a server. In this instance you don't want your cell responsible for triggering a download since the cell will then also be responsible for monitoring the progress of the download. Since these cells are reusable this becomes more problematic as the cell becomes reused.
So yes, in a simple case where you need to bind data to a cell it makes sense for the cell to be responsible for configuring its subviews with the relevant data.
Regarding prepareForReuse a casual glance at the documentation details
Discussion If a UITableViewCell object is reusable—that is, it has a
reuse identifier—this method is invoked just before the object is
returned from the UITableView method
dequeueReusableCellWithIdentifier:. For performance reasons, you
should only reset attributes of the cell that are not related to
content, for example, alpha, editing, and selection state. The table
view's delegate in tableView:cellForRowAtIndexPath: should always
reset all content when reusing a cell. If the cell object does not
have an associated reuse identifier, this method is not called. If you
override this method, you must be sure to invoke the superclass
implementation.
I have the following UITableViewCell (well, subclassed).
With didSelectRowAtIndexPath it is possible to capture that a cell has been selected in UITableViewController. My problem occurs due to the fact that directly pressing Choose User bypasses the selection of the cell.
How could I allow my UITableViewController to be aware that UITableViewCell foo has been pressed even if the user immediately hits Choose User?
N.B. I don't need the Selection capability per se, this was just by method of knowing that a user had tapped within a cell area.
You could just call the method directly. If we say that for each Choose User button we are setting the row number as the tag and assuming that you don't have sections so everything will happen in section 0 we could do.
- (void)hitChooseUser:(id)sender
{
// Do whatever you want for when a user hits the `Choose User` button
// Code......
// Then do this at the end or whenever you want to do it.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[sender tag] inSection:0];
// Also assuming you have created you have created you UITableView correctly.
[self tableView:myTableView didSelectRowAtIndexPath:indexPath];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Do whatever it is you want.
}
I also found this link that may help you Manually call didSelectRowatIndexPath
You could also disable the user interaction with the cell itself by setting userInteractionEnabled: to NO for each cell in cellForRowAtIndexPath: so didSelectRowAtIndexPath: will only get called when you want to call it manually.
Do not call didSelectRowAtIndexPath: It is a UITableViewDelegate method and, where possible, should be used as such (meaning let the UITableView send messages to it). In addition, it creates an unnecessary dependency on UITableView implementation.
That being said, in order to achieve shared behavior that is performed either on button click, or on row selection, refactor it out into a common method that is not coupled with UITableViewDelegate
For example:
-(void)doSomethingCommon {
//do shared code here
}
-(void)chooseUserButtonPressed:(id)sender {
[self doSomethingCommon];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self doSomethingCommon];
}
And if your UITableView shows more than one of these rows, for which you depend on knowing which corresponding model object is related to the cell, than you can use the tag property on UIView subclasses (usually something in your cell) to mark the row that the object is shown in.
I have a method constructed like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//some table related stuff
}
However I cannot call this, so I basically copied and pasted the whole function and renamed as:
- (void)jumpCountry: (NSIndexPath *)indexPath {
//some table related stuff
}
and calling this method by using:
[self jumpCountry:countryIndex];
However my class looks ugly (and not preferred) because it has got the same two methods. So, how can I call the initial method directly(I know that it is assigned to a button which invokes that method). I am using iOS6.1. Basically, the reason why I want to directly call is I have another thread that listens notifications(from location services), once a notification is received, the table view should be changed. The notification itself already searches for NSIndexPath, so there won't be any problem with that.
You can just put
[self jumpCountry:countryIndex];
to your method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
This method a delegate method of table view from UITableViewDelegate protocol and it gets call when user select a row in UITableView. you should not call this method by your self.
instead of you can create your method and do whatever you want to do and call it in viewDidLoad or any method.
To call programatically use
[tabelView selectRowAtIndexPath:scrollIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
Here scrollIndexPath is the indexpath of row to be selected
For creating indexpath
NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
I hope someone can help...
I've setup a static UITableView Controller with four sections. The top two sections are for inputting data. The third section has 1 cell and contains a save button.
The final section has 6 cells and I want to be able to populate these six cells with the data from the first two sections, in the long run anyway...
So i tried to a small example, but it's not working. The console returns the following error:
UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
* First throw call stack:
Here's an example:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
NSIndexPath *EditRow = [NSIndexPath indexPathForRow:1 inSection:4];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:EditRow];
NSString *test = #"Test";
cell.textLabel.text = test;
NSLog(#"Index Path: %d", indexPath.row);
NSLog(#"Cell ID:, %#", CellIdentifier);
return cell;
}
So my question comes down to.. How do I update a specific cell in a specific section?
Thanks
When I implement static cells, I delete the cellForRowAtIndexPath, numberOfSectionsInTableView, and numberOfRowsInSection methods from the generic UIViewController stub that Xcode generates; these seem to be necessary only when you're dealing with dynamic cells.
Then, by control-dragging in the storyboard, I create named outlets for all UI elements in the cells that need to be set programmatically; in my case, these are usually UILabels. In the viewDidLoad method of the controller, I set the appropriate initial values of these outlets, for example:
self.nameLabel.text = self.dataSource.name;
Once you've got this wired up, you can change any of the values at will.
I hope that this works for you. I'd be happy to learn from anyone who has a better (more appropriate?) way of doing this.
Simply call - [UITableView (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation], then your data source will be called for the appropriate index paths.