Preserve Cell Image After Scrolling UITableView - iphone

I have a custom UITableViewCell which acts as a check list. As the user clicks on the selected item, it goes from red to green indicating a selection, however as the users scrolls further down the list, once they come back up to the top, the image has changed back to it's default red value.
How do I go about preserving the state of an image as the tableview recycles cells?
Thanks

Sounds like you are using the UITableViewCells to store the state of your table data. This is the wrong approach because the cells are reused. You should keep state in an separate 'data store'. This can simply be an array you keep in memory in your UITableViewController subclass or something persistent like SQLite or Core Data. This state is then transferred back to the cell when the table view asks you to in tableView:cellForRowAtIndexPath:.

I had the same problem. Use:
MyTableViewCell* cell = [tableView dequeCellWithReuseIdentifier:#"MyTableViewCell"];
if (cell == nil) { /* create cell */ }
/* and now you reset the properties of that cell */
cell.text = [myCellText objectAtIndex:indexPath.row];
The key here is to reset the properties of dequeued table cell based on the indexPath. This does mean you will have to keep around enough state to re-create any table cell--including the images you are using.

Related

UITableView-didSelectRowAtIndexPath Problems when call didSelectRowAtIndexPath

I have a UITableView with CustomCell, in my CustomCell I added UIImage,UILabel...
But, when I selected cell in row 1 and remove UIImage on it : [[cell.contentView viewWithTag:TAG_FOR_TOPIMAG]removeFromSuperview]; However, cell at index 7 was changed same cell 1.
Table view reuses cells (dequeueReusableCell...).
So, the same cell (view) may be used for more than 1 index in the table.
After you change a cell's layout (add/remove/modify subviews) then this cell (view) will appear modified each time it is presented on the screen - even if it represents different index each time.
For example, in your case you probably have maximum of 6 cells visible in the same time on the screen.
Therefore, you have modified some cell (it was displayed for index 1 once you have modified it), after that you have scrolled the table, the cell with index 1 has disappeared from the screen, then the cell with index 7 should be presented (during the scrolling). The table will use the same cell (exactly the same UITableViewCell object) that was used for displaying row with index 1.
But you have removed the image from it...
You have to keep it in mind all the time when you work with tables.
In your case I would suggest you to set the visibility of the image view to hidden and restore it in each tableView"cellForRowAtIndexPath: method (NOT inside the if (cell == nil) statement - after it).

UITableView renames every eighth cell

I have a UITableView in my MainViewController. When a user taps a cell,
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
selectedRow = indexPath;
....
[self performSegueWithIdentifier:#"OtherViewControllerSegue" sender:self];
}
and they are taken to another UIViewController (let's call it OtherViewController). In OtherViewController, the name for the selected cell is set. When OtherViewController is dismissed, it updates the cell in MainViewController with the new name:
[[[mainvc.myTableView cellForRowAtIndexPath:mainvc.selectedRow] textLabel] setText:namecell.textField.text];
[self.navigationController popViewControllerAnimated:YES];
This all works fine until I have more cells than will fit on the screen. If there are more cells than will fit on the screen (8 for iPhone or 16 for iPad), then this will also set the name for every eighth or sixteenth cell respectively. Any ideas on what I am doing wrong?
Update:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [pointsTableView dequeueReusableCellWithIdentifier:#"myTableCell"];
return cell;
}
This is due to cell-reuse and you are mixing up your model with your view (in the MVC context).
A table-cell is a transient thing, once it goes off the screen it is reused (instead of creating new cells) when another cell is needed. This is what the dequeueReusableCellWithIdentifier: method does.
This means you can't store data in there and expect it to still be valid later on. In this example you are trying to store the name in the table cell. The reason to set a property (like the label text) on any view object is purely for display, not for storage. So to solve this problem you should maintain a list of objects in your model (this could be in separate classes or in an array in your mainvc object for example). Then in cellForRowAtIndexPath: you should set the label text every time - even when there should be no label you need to set it to nil or an empty string because the cells are re-used it might contain something from the last time it was used.
Update:
Instead of calling cellForRowAtIndexPath: yourself and setting its text, you should set the text in your model using a method or property in your controller and then tell the table view to reload that cell. The code might look something like this:
// This code is in where you want to set the text from
[mainvc setText:someText forIndexPath:indexPath];
.. and in your main view controller:
- (void)setText(NSString*)newText forIndexPath:(NSIndexPath*)indexPath
{
// Store the text in your model here...
...
// If the view is loaded, the table view should reload the cell.
if(self.isViewLoaded)
{
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
The table view will then call cellForRowAtIndexPath: where the text will be set correctly. This may seem a little convoluted at first, but when you get used to using the Model-View-Controller design pattern you will find that keeping the jobs of each MVC component separate like this will mean your code is tidier, easier to understand, has less bugs, is easier to update/extend, etc.
You're trying to store data (the new name) in a view (the cell's label). What's probably happening is that when you re-use cells in the data source's cellForRowAtIndexPath method, some of them are ones that have had this text set for them and it's still there.
The better idea is to make your changes in whatever array you use as cell information and then reload the table view to make the changes visible.
As I suppose, you shouldn't call cellForRowAtIndexPath by yourself. It can be called to create cell, not to change it.
You can update your table by passing needed string to the first view via delegate, for example. And on the event (user sets the name) you can update all table and set needed names to cells.
Hard to say exactly what the problem is, but one possible solution might be this:
Make sure that in your cellForRowAtIndexPath you are initializing the cells like this:
// Create the Cell
static NSString *recordCell = #"pickerTableCell";
cell = [tableView dequeueReusableCellWithIdentifier:recordCell];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:recordCell];
}
I know this is primarily a memory solution, but might gelp here too.
Also, look through your code and check how you are determining which cell is renamed. You could be accidentally calling the rename on more than one cell without realizing it

iphone: changing status of several segment controllers at once in custom cell

I have a tableView with the custom cell (see image below.)
Taking a three row table as an example, if a user changes segment controller in row 0 to "Yes," can I automatically change the segment controllers in rows 1 & 2 to "No?"
I am using the following to detect a segment change:
- (void)seg_changed:(id) sender {
cell=(switchCell*) [[sender superview] superview];
UITableView *table=(UITableView*) [cell superview];
NSIndexPath *path=[table indexPathForCell:cell];
NSLog(#"been pressed %d si %d",path.section, path.row);
}
Much appreciated.
In this method you call just tell those other segment controllers to set their values to "NO". The hard part is figuring out where those two other controls are. You have to do the hard work of tracking them.
If your design ensures there are always two more cell with segmented controls you can just access the correct cells by incrementing the path.row value.
This change can tell your data model that a value has changed, the model object then updates the associated values, and notifies the cells displaying those other values.
You can add an array to this cell class that keeps track of what other cells should be modified with this change.
Edit: (to respond to a comment) To change the setting displayed on the segmented control just set the property selectedSegmentIndex of the UISegmentedControl to the appropriate value. "Yes" should be 0, and "No" should be 1.

how to read value from table view cell in iphone without selecting any row

thanks in advance
i want to read cell value from tableview but without selecting any cell.
let me explain first
in my table view each cell have some value when tableview is drown fist time this all value is coming from database.
then later through didselectedRowAtIndexPath for example i select first cell value and take it in label in another view and change it a when i click save button on new view then my new value is coming back in first view in table view cell correctly this all operation is correctly working.
but my problem is that after changing some cell value i want to read all the data that display on table view cell but without selecting any cell in didselectedRowAtIndexPath or any other method i read this cell value on my save button on this view and after read this value i again save it in database
Geez, I think if I were you I'd go the other direction. You surely have a data structure of some sort (an NSArray, quite likely) that you used to populate your tables with data in the first place, right? Why not just get it from there, rather than getting your mitts on the UITableViewCell and digging into its view hierarchy to find the label you want?
The didselectedRowAtIndexPath method is just a means to tell you what point in your array to read data from. You have to create an array of some sort to build the table from. If you know you want to read data from the first cell, simply read it directly from the array populating your table. Doing this at the end of viewDidLoad would be appropriate.
Try...
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if its a custom cell use
CustomCell *cell = (CustomCell *)[tableView cellForRowAtIndexPath:indexPath];
NSLog("%#", cell.property):
You can get cell for only visible rows without clicking them as
NSArray *visibleRowsArr = [tableView indexPathsForVisibleRows];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:[visibleRowsArr objectAtIndex:0]];
and from this cell you can get values for a tag which you have to specify while creating a cell.

Reloading UITableViewCell on select

Okidoke. Here's my problem: I have a series of complex UITableViewCells set up to display stories from a news feed (yawn). What I want to happen, is for the cell background image and height to change on selection (as a means of marking that the story has been read).
Now, prior to setting up dequeueing, I was able to do this with a simple [self.tableView reloadData]. That seems to be a no-go with dequeued cells; reloading the table view does not redraw the cells to match their changed state.
I've tried reloadRowsAtIndex- and while this works - beautifully - for the first cell a user clicks on, it goes wonky after that point: sometimes the cell reloads correctly, sometimes not.
Obviously, each story is an NSMutableDictionary object. I'm using an NSNumber object to track whether or not a story has been read.
I would post the code, and I will if anyone asks, but I'm looking for a generic solution that could be implemented in any UITableViewController (share the love).
So, simply put: how does one reliably redraw complex cells on selection?
Try giving each cell a unique ID in order for dequeuing to work, your cells should be coming back with their changed states if you use a unique id for each cell, so 20 cells = 20 ids, hope this helps
Assuming you have the index path, you can access the cell and manipulate it directly:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// handle the selection...
UITableViewCell *cell = [tableView cellForRowAtIndexPath: indexPath];
if (nil != cell) {
//
// now update the cell to reflect the new state
//
}
}