UITableView renames every eighth cell - iphone

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

Related

UITableView cell redraw causes text to overlap

I do following in cellForRowAtIndexPath
NSString *cellIdentifier = #"Cell";
LibraryCell *cell = (LibraryCell*)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell != nil) {
[cellIdentifier release];
[self setItems:cell forRowAtIndexPath:indexPath];
return cell;
}
cell = [[[LibraryCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier] autorelease];
[self setItems:cell forRowAtIndexPath:indexPath];
return cell;
[self setItems:cell forRowAtIndexPath:indexPath]; changes only some UILabel's value inside the corresponding cell.
So now when I edit UITableView and delete let say the first row, the UILabel's value is no redrawn completly, i.e. the old value remains and new value is drawn overlapping the old one.
Why this happens and how to fix it ?
I guess you misunderstood the cell identifier concept.
It is only used to distinguish what you might call "stamps", used to print a cell's appearance on the screen. So you would most probably only need a single cell identifier.
It helps the system to cache instances of the "stamps". When cellForRowAtIndexPath is called, you only have to pick which kind of stamp you want to use. If you created an instance of the correct one before (i. e. you get one back when asking for it using the cell identifier string), you only need to change the label texts etc. and then return it. In real life this might be likened to one of those date stamps where you can change the date by turning the little knobs on the stamp. This is what you would do by assigning a new text to the label contained in the cell.
You instead seem to be creating a stamp for each index in your model by concatenating the string value, effectively creating as many instances as there are rows in your model. Apart from being unnecessary it might also cause memory pressure and stuttering, because it counteracts all sorts of optimizations the UITableView has.
I recommend you read up on Apple's documentation or see iTunes U (here)
for the Stanford courses on iOS development. It gets explained very clearly there.
First of all, You should use the same name for your cellIdentifier
Please refer to the UITableView Class reference
http://developer.apple.com/library/ios/#documentation/uikit/reference/UITableView_Class/Reference/Reference.html#//apple_ref/occ/instm/UITableView/dequeueReusableCellWithIdentifier:
If you cannot get a reusable cell, then create it. After that update your cell.
The code should be like this
LibraryCell *cell = (LibraryCell*)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
// create a new cell
...
}
// Update cell
...
return cell;
Actually I figured out ! The problem was coming from setItems:forIndextPath method.
It was creating UILabel locally and releasing it. So every time it was drawn over the last text text. Making the UILable instance variable solved the issue.
PS. The code originally was not written by me, I'm just fixing it :)

Delete Row from UITableView Outside TableView Delegate

I am trying to delete a row from UITableview outside the delegate method. I am calling a method when I click a button inside a table cell and trying to delete the row inside that method.
Here is the function I am using
UIButton *btn = (UIButton*)sender;
int tag = btn.tag;
UITableViewCell *buttonCell = (UITableViewCell*)[[btn superview] superview];
NSIndexPath *indexPath = [self.msgTbl indexPathForCell:buttonCell];
[deleg.rmessages removeObjectAtIndex:buttonRow];
[self.msgTbl deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];NSInteger buttonRow = indexPath.row;
[self.msgTbl reloadData];
Using this one or two rows get deleted but after that its crashing giving exception
Number of rows before and after deletion must be same
How can I do this in ios?
Thanks
Your problem is that the data that is being taken to populate your table isn't consistent with the table after deleting the cell. Make sure your dataSource methods provide the correct data after doing this (for example, if it is an array of objects you are using to populate the table, you must remove the object from the array as well)
The root issue is that this method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
Must return the correct number of rows, and it isn't. When you remove the item from deleg.rmessages, is this the same object that is being used to supply the return value of the above method? (Something like [deleg.rmessages count]?)
Also, in my experience that exception often gives you more details, in particular:
How many items it had before
How many were added/deleted
How many it expects to have vs. how many it does have after the reload
Do you see anything like this being mentioned? If so, it would be worth including in your question.
Sidenote:
It's a bad idea to rely on:
UITableViewCell *buttonCell = (UITableViewCell*)[[btn superview] superview];
To return the UITableViewCell. You appear to assign the tag of the button to a local variable, but never use it. (Maybe this would be a good place to store the index of the UITableViewCell, and then subclass the cell to maintain an ivar to the button?) This is only part of the problem.

iOS SDK: How to get a cell in a table view that is not visible?

How do I get the cell for an indexPath which is not currently visible in the table? (cell is out of range)
Code for getting my cell:
NSString *name = [[(ELCTextfieldCell *)[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]] rightTextField] text];
-cellForRowAtIndexPath... returns nil because the cell at the required indexPath is out of range. So how do I get the correct cell and not nil?
The UITableView only keeps the visible cells. If you need one that isn't visible you have to call the tableView:cellForRowAtIndexPath: of the UITableView dataSource. So, if self is a class that is the dataSource:
UITableViewCell * cell = [self tableView:table cellForRowAtIndexPath:indexPath];
This is not how it works. You need to grab and store the information as soon as it is entered or changed. It may easily get out of scope and you cannot guarantee your cell lives long enough. Well, technically you can hold onto it (and always return the very same cell for the same index path), but I'd question that design.
UITableViewCells are made to be reused/recycled in a way that the users won't need to create more cells than the number of visible ones.
You usually don't need to access a cell that is not visible. It should be enough you access your datasource and get/set the correspondent data there. Cells are for showing some state of the datasource. Not the datasource itself :)
Edit:
You said you need some information (text) from one cell above, right? If you use cellForRowAtIndexPath: method the cell will be recreated but you might not get the text that was in the textfield. The reason? because probably you didn't save it somewhere else. If you did save it, then access that directly instead of going through the cell.
In continue to #Raphael's answer, Here is the (working) swift 3 solution:
UITableViewCell cell = self.tableView(self.tableView, cellForRowAt: indexPath)
Is the information to fill your table coming from an array? Could you not pull that directly out of the array at 0 index, same as your cellForRowAtIndexPath would presumably retrieve and fill that cell when it is displayed?

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
//
}
}