UITableView cellForRowAtIndexPath invoked multiple times if it's a manual call? - ios5

If I have UITableView as a property of a UIViewController and I'm manually accessing a cell at a particular row with [tableView cellForRowAtIndexPath:indexPath].
On one method invoke, should I expect to see multiple calls to:
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Each of my UITableViewCell has a UITextField whose text data I'm trying to access.
Here's what I'm doing:
for (int section = 0; ix < countOfSections; ++section)
{
NSInteger countOfRowsInSection = // attained
for (int row = 0; row < countOfRowsInSection; ++row)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
// the follow call seems to elicit multiple invocations
// on my table delegate's
// - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
UITableViewCell *cell = [self.tableview cellForRowAtIndexPath:indexPath];
// Access some custom data
}
}
Is there a better way to access all of the data stored in the UITextField each of the UITableViewCell for all sections & rows?

You really shouldn't be trying to use views for data storage. In whatever you're using as the table's data source there should be an array (or other structure) of objects that contains the data that provides content for the cells when tableView:cellForRowAtIndexPath: is called.
If you need other access to that information, you should be getting it directly from the data structure rather than the display.

Related

custom UITable cell how to build with unique included object state?

So I has custom table cell defined via builder and loaded via nib (and it has a property of its idexPath) and it has 2 buttons. I want to show table with dynamically changed state of those buttons. IE 1st cell - both enabled, 2nd cell - both buttons disabled, 3rd - 1st btn enabled and 2nd btn disabled and so on.
Now if I use 1 reuse identifier all the cells will look the same which I don't want. I want every cell has its own view which means unique reuse id for every cell.
But how to reach this? If I'll create some unique cellId at
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
which will be a string then I could not create the same string in meaning of objects. I could create string with same text but this will be another object so I couldn't get the previously created cell with such cellId again via reuseId. So i cant change buttons state of one cell and update it then with
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:path] withRowAnimation:UITableViewRowAnimationNone];
but only the [tableView reloadData]; will work.
I have a feeling that you're only setting the state of your cell's buttons when you first create the cell with initWithStyle:reuseIdentifier:. This is the wrong way to go about things. You need to set the state for your cells in every call to cellForRowAtIndexPath, whether they're being re-used or not. In your case, if each cell has the same UI (two buttons) then they should all share one reuseIdentifier. Your datasource should be responsible for maintaining you cells' states, not the UITableViewCell objects.
It's the difference between this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
myCustomCell *cell = (myCustomCell *)[myTable dequeueReusableCellWithIdentifier:#"myCellIdentifier"];
if (cell == nil) {
// Load cell from nib here and set the cell's button states based on indexPath
}
return cell;
}
and this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
myCustomCell *cell = (myCustomCell *)[myTable dequeueReusableCellWithIdentifier:#"myCellIdentifier"];
if (cell == nil) {
// Load cell from nib here
}
// set the cell's button states based on indexPath here
return cell;
}

Linking UITableView with a NSMutableArray

I've been trying to fill a UITableView with data from a NSMutableArray. I have a View-Based application project and so far I've tried every tutorial I found but so far, it just doesn't work. I linked the dataSource and delegate to the File's Owner, I added the procotols UITableViewDelegate and UITableViewDataSource to my UIViewController, I create an array containing 3 strings just to try and debug, everytime I load the app, the table stays empty.
I'm not sure what could be my error, or where I should look to find the problem, if you guys got any idea that would be nice.
Thank you
-(NSInteger) tableView:(UITableView *)table numberOfRowsInSection: (NSInteger)section
{
return [yourArray count];
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [yourArray objectAtIndex:row];
return cell;
}
That should populate the table from an array. I've used it several times in my app.
I guess you did forget to call -reloadData on the table view.
Make sure the dataSource and delegate are properly connected to the view controller, also make sure your UITableView is property connected with the outlet if you are using storyboards or a xib file.
If nothing works I recommend you to use XLData that makes UITableView data loading much more simple to implement.

Refreshing UItableView Data from a frequently changing NSArray

i've a UITableView and I'm reading a data from a web service.
the data from the web service may change at any time, so i have to refresh my data periodically.
i've managed to refresh the data and store it in an NSArray, but the UITableView won't display those data.
i tried
[myTableView reloadData];
but it have no effect.
EDIT:
i've implemented all the methods to load the data from an NSArray to the UiTableView.
this works when the NSArray is initialized in the ViewDidLoad.
but if the NSArray changed while the application is running, the UITableView Will not display those changes.
You need to implement the UITableViewDataSource delegate protocol, specifically - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Use this method to set up the cell. You can use the row property of indexPath to determine which cell you are setting up and provide it with data from your array.
For example
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
id item = [dataArray objectAtIndex:indexPath.row]; // Whatever data you're storing in your array
cell.textLabel.text = [item description]; // Substitute this for whatever you want to do with your cell.
}
EDIT:
reloadData should call
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView and
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
to see if there are any cells to be drawn. Make sure you implement these methods as well and return non-zero values or your table view won't try to draw any cells and - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath won't be called.
Parse
You may also be interested in this library which claims to make remote data-driven tables a lot simpler.
You have to call the function where you put the content into the table.

UITableViewCell - Load 25 cells - don't reuse

I have 25 table cells and I want to load all of them together, without reusing them.
Any Idea?
If you want to avoid recycling table cells, you can just avoid calling dequeueReusableCellWithIdentifier: in your tableView:cellForRowAtIndexPath: method.
If you want to do a one-time initial load of all your cells, you can do something like this in the init method of your table's data source:
// myCellArray is an instance var of type NSMutableArray.
myCellArray = [NSMutableArray new];
for (int i = 0; i < 25; ++i) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0];
UITableViewCell *cell = [self tableView:tV cellForRowAtIndexPath:indexPath];
[myCellArray addObject:cell];
}
This will keep the cells in memory, since they'd be retained by myCellArray for you.
To be more efficient, your cellForRowAtIndexPath: method can be something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([myCellArray count] > indexPath.row) {
return [myCellArray objectAtIndex:indexPath.row];
}
return [self createCellAtRow:indexPath.row];
}
However, you want to be careful about using more memory than you think is needed, and avoid taking too much time initializing your table. In many cases, your app is likely to appear faster and use less memory if you just use recycled cells in the standard way (as suggested, for example, in the UITableViewDataSource docs).
You can't load them all together. But for "not-reusable" cells, you can make unique cell-reusable-identifiers. So, it system needs the same (first, second, third) cell — it can get already created one. And for new cells they will be created as needed.
And if you reeeeeally need to load all the cells at once, you can call
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
in viewDidLoad for every indexPath you needed.

UITableViewCell: how to verify the kind of accessoryType in all cells?

I have a UITableView in that some cells are marked with UITableViewCellAccessoryCheckmark at the initialization of the view.
When the user selects another row, I have to check if the maximum number of selected rows was achieved before. To do that, I used the code bellow:
- (NSInteger)tableView:(UITableView *)tableView numberOfSelectedRowsInSection:(NSInteger)section{
NSInteger numberOfRows = [self tableView:tableView numberOfRowsInSection:section];
NSInteger numberOfSelectedRows = 0;
for (int i = 0; i < numberOfRows; i++) {
UITableViewCell *otherCell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:section]];
if (otherCell.accessoryType == UITableViewCellAccessoryCheckmark) {
numberOfSelectedRows++;
}
}
return numberOfSelectedRows;
}
If my number of rows is, as example, 20, the variable numberOfRows is setted correctly with 20. Lets say that 13 rows already are marked with UITableViewCellAccessoryCheckmark. So, numberOfSelectedRows should be 13 after the loop, but only the marked and VISIBLE cells are considered. So, if I have 9 cells showed and 7 are marked, the numberOfSelectedRows returns 7 instead of 13 (but the for iterate 20 times, as expected).
Is this a correct behavior of UITableView or it is a bug of iPhone simulator?
Thanks in advance.
Yes, it works as designed. You should never store model data in your views. UITableView knows nothing about the data, it only displays cells (and throws them aways as soon as they scroll off the screen). You need to store the checkmark state of each cell in a model object (e.g. an array) that you then access from your view controller.
This is correct behavior.
The UITableView is not a list. The system caches cell that are off screen to save memory and CPU and they can not be iterated over in a manner that makes sense.
Ok, you should keep track of the model/data and the tableView will keep track of displaying it. I have had some problems with this until I accepted that uitableView is not a list:)
So, have an array of objects that each corresponds to the data in the a cell. When building the individual cells like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"categoryCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
Item *item = [self.itemList objectAtIndex:indexPath.row];
[cell.textLabel setText:[item itemBrand]]; //notice that here we set the cell values
return cell;
}
The when a user clicks you change you model like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"IndexPat.row%i", indexPath.row);
Item item = (Item*) [self.itemList objectAtIndex:indexPath.row];
//change the state of item
}
This way the tableView will update to resemble the model/data, you just managed the model.