- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"CellName-%d", [indexPath row]];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
// etc
}
}
I want to clear all the cells I've stored up since the table data has changed so they need to be regenerated. I am storing all the cells in this way deliberately.
I am wondering is there a way/method to dumb all the cells that are alive, that are related to a particular table.
Regards,
-Code
You can't, also you when I look at your code you aren't using any of the dequeued cells since you create a new cell for every row in you tableview.
This would mean then if you have a 1000 cells in your table a 1000 tabelviewcells are created. This will make you tableview very slow, also it will take up to musch memory.
When cells look the same should all give then the same cellIdentifier, then just fill the cell with the data from you data source.
You should only create a new cell when the dequeueReusableCellWithIdentifier: does not return any cells.
Then when [self.tableView reloadData] is called you just read the new value from the datasource and fill them in in the tableview cell.
You datasource is the place to stored state and not cells, because I can't think of any other reason to have your datasource work this way.
while ([tableView dequeueReusableCellWithIdentifier:#"reuseid"]) {}
i have been battling with this too, the solution is to remove the table view from superview and release it completely, so the tableview is dealloc-ed
[self.tableView removeFromSuperview];
And than recreate a new TableView... this is kind of brutal, but i needed this to remove costly cells with uiwebviews
I think UITableView has method [tableView reloadData] which clear all cells. When i make app in which data is appended in tableview. i calle this method.
Related
I am using [self.messageList scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO]; in cellForRowAtIndexPath and it is working fine, when a row is added in table array then it auto scroll upside but the problem is that when I try to see the top rows then it automatically come again last rows. And I am not able to see my all rows. Please let me know where I use this or any other approach to do this.
cellForRowAtIndexPath method is not called for all cell that you have. It is only called for visible cell in your TableView. When You Scroll UITableView then it ask to UITableView that you want to use reusable cell or add to new one. generally we use reusable cell so, it is use 1st no of cell and add at end of cell . it is not create another new cell at the end. so, when you scroll your UITableView it not scroll to end of cell it indexPath is similar to indexPath of first no of cell. so you can not scroll your UITableView at to Down.
use following code may be helpful for you.... :)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:#"S%1dR%1d",indexPath.section,indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
[self.tblView scrollToRowAtIndexPath:indexPath atScrollPosition: UITableViewScrollPositionTop animated: YES];
// your code
}
// your code
return cell;
}
If you write this code in your cell for row at index path, then it will get called every time you try to scroll manually coz this message is called to draw your cell and you will b directed to the row you specified. Try checking your indexpath in a IF--Else block and scroll only when a certain action happens.
cellForRowAtIndexPath is a method for creating a cell to view .When you scroll this method is called when a cell is viewed.So each time the method called the [self.messageList scrollToRowAtIndexPath:indexPath method(which is called inside cellForRow) is also called and the table scrolls down to that row!!!
at first,i delete one cell in tableView,then i want to add another new cell in the tableView,but the new cell is not new,it is just the cell which i delete. i know what dequeueReusableCellWithIdentifier means.i think when delete one cell,the cache in dequeueReusableCellWithIdentifier is also deleted.why not?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"table reload");
PDFModel* tempmodel=[PDFArray objectAtIndex:indexPath.row];
NSString * CellIdentifier=tempmodel.PDFName;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSLog(#"cell %x",cell);
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
//custom the cell
cell.text=...
}
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle==UITableViewCellEditingStyleDelete)
{
[PDFArray removeObjectAtIndex:indexPath.row];
NSMutableArray* array=[[NSMutableArray alloc] init];
[array addObject:indexPath];
[self.tableView deleteRowsAtIndexPaths:array withRowAnimation:UITableViewRowAnimationRight];
}
}
There's no reason to delete the UITableViewCell since it can be reused. That cell won't be displayed until it is processed again by cellForRowAtIndexPath, so it won't show up with the old data. The laid-out cell, though -- the view and all of its subviews -- can be reused after new data replaces the old data.
Think of it like each cell is a whiteboard (dry erase board) where it's cut down to a certain size and all of the basic information areas are etched into the surface: a place for some text, another area for a number, a special space for drawing a picture, etc. In tableView:cellForRowAtIndexPath: when you check whether the cell == nil, you're trying to find out if there are any already-etched whiteboards available that you can erase and write the new information on with your dry erase marker. If there aren't then it cuts out and etches a brand new board to write information on, but if there is one that can be erased then it just wipes off the old information with an eraser and writes in the new information. As you can imagine, this saves a lot of time.
When a user is scrolling the table what's actually happening is that when one cell (whiteboard) scrolls off the top and is no longer visible, the system erases the data and moves it to the bottom of the stack; it will be the next one to scroll into view. As a result the system only has to create as many whiteboards as can be visible at any one time -- the ones off-screen don't actually exist.
When you delete a cell all you're really doing is telling the table to (a) stop displaying that cell, and (b) setting a flag so the table can use the eraser to wipe off the old data but still reuse the whiteboard.
Creating cells can be very costly in terms of computing power. If a UITableView had to create a new cell every time one came into view while the user was scrolling -- in the analogy, if it had to cut out a new whiteboard and etch all of the areas into it every time one came into view -- the scrolling would be very jerky and look terrible. By reusing cells, replacing just the changing contents, the table can move smoothly as the user interacts with it.
Your question is a bit weird, but I guess you're wondering what the following code does?
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
What the code does, is that it tries to retrieve a cached UITableView cell. This is to reduce memory usage on the iPhone.
Once you delete a cell, you should also remove the data from the datasource (usually an array), e.g.:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle==UITableViewCellEditingStyleDelete)
{
// remove object from array ...
[dataArray removeObjectAtIndex:indexPath.row];
}
}
(Remember: you can only remove items from an NSMutableArray at runtime, not from a NSArray)
If the cell gets redrawn, you will first check which cell needs to be redrawn, then you retrieve the appropriate data from the datasource, this is all done by the UITableViewDataSource delegate methods. You only have to make sure to retrieve the right content from the array and update the cell's content on redraw, e.g. like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
// no cached cell is found, create a new cell ...
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// add some defaults for cells in the initializer,
// stuff that's done here should be important for
// all displayed cells, for example background image.
}
// update the cell's content, e.g.: - it will only display content that's currently in the array, deleted objects shouldn't exist in the array at this point
NSString *title = [dataArray objectAtIndex:indexPath.row];
[cell.textLabel setText:title];
return cell;
}
I think You can use
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell%d",indexPath.row];
instead of
NSString * CellIdentifier=tempmodel.PDFName;
please replace it & check it. I think it's helpful to you.
You seem confused by the difference between deleting a cell (an objective-c UI object) and the data it displays.
When you allow a user to delete a cell in the user interface you need to delete the corresponding data from your data model
The implementation of UITableView is highly optimized in iOS and it caches cells (UI objects) to improve performance. You don't really need to be concerned about this as your table will be optimally loaded and displayed based on your data.
In tableView:cellForRowAtIndexPath: you do the following:
Attempt to dequeue a cell instance. This takes advantage of caching done by UITableView
If dequeue does not return a cell, create a new one.
Once you have a cell, configure it with the appropriate data from your data model
When a user chooses a delete action from a cell, delete the data from your data model. optionally, you can animate removing the cell from the table or simply reload the whole table.
The specifics of #4 depend on the user interface you want and how you've configured your data model.
The Apple docs are pretty good about explaining this.
http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/TableView_iPhone/AboutTableViewsiPhone/AboutTableViewsiPhone.html
I have a UILabel in a custom UITableViewCell that gets resized when the device is rotated. The text in this label needs to be recalculated after the rotation because I am cutting it down to size and appending some text at the end.
E.g. the datamodel has: "This is a run-on sentence that needs to stop."
In portrait mode it becomes "This is a run-on sent... more"
In landscape mode it becomes "This is a run-on sentence that... more"
From (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
I am able to access the visible UITableViewCells and update the descriptions.
The problem seems to be that there are UITableViewCells that are cached but I can't get to. When I scroll the UITableView after a rotation, one or two cells that are below the visible area after the rotation don't have the correct text in the label. So they haven't been rendered via (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath - but they weren't returned by [tableView visibleCells] (or via looping through all views returned via [tableView subViews]).
I've tried to access the "extra" cells via this method:
for (int index=max + 1; index < max + 3 && index < [cellTypes count]; index++) {
NSIndexPath *updatedPath = [NSIndexPath indexPathForRow:index inSection:0];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:updatedPath];
if (cell == nil) { continue; }
[self updateCellForRotate:cell forRow:index];
}
(where max is the biggest row returned from visibleCells) but cell is always nil.
Is there anyway to flush the cache of UITableViewCells so that they don't get re-used? Or to access them so I can update them?
Thanks!
Two things.
First. In your didRotateFromInterfaceOrientation method you can simply reload the visible rows like so:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation) fromInterfaceOrientation
{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
NSLog(#"didRotateFromInterfaceOrientation:%d",fromInterfaceOrientation);
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationNone];
}
Then I would recommend you add either the interfaceOrientation number or simply the table width to the dequeue cell name that way the tableView knows that cells in one rotation are different from those in another. Like so:
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath withType:(NSString *)s_type
{
UITableViewCell *cell = nil;
// add width of table to the name so that rotations will change the cell dequeue names
s_cell = [s_cell stringByAppendingString:[NSString stringWithFormat:#"%#%d",#"Width",(int)tv.bounds.size.width]];
NSLog(#"%#",s_cell);
cell = [tv dequeueReusableCellWithIdentifier:s_cell];
if( cell == nil ) {
cell = [[[UITableViewCell alloc];
initWithFrame:CGRectZero reuseIdentifier:s_cell] autorelease];
}
}
Firstly, to reload all of your table cells use [self.tableView reloadData]
Secondly, add the line of code that is responsible for the shrinking inside the (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method.
Example:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Some identifier and recycling stuff
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
//Make labels smaller
}
else {
//Make them bigger
}
}
Or you can just call your updateCellForRotate:forRow: method when making them. But I'm not sure how that function works, so I can't be too specific.
When you create the cell in cellForRowAtIndexPath:, add it to an array. Then, loop through the array, updating the text as necessary.
Hope this helps,
jrtc27
EDIT:
You say they are custom cells - could you not update your text in your UITableViewCell subclass?
So, I was having (what I think was) a very similar problem recently, and none of the posted answers helped me, I'm sorry to say.
My issue was that I deliberately resized and repositioned the UITableView upon rotation, and I did that programatically. The table cells in portrait took up the width of the view, and in Landscape were made somewhat higher but less wide. I then repositioned the elements of the cell depending on the orientation we'd come to.
Upon application start, the first viewing of the table was fine. Then I rotated and found that I appeared to have two instances of some elements, and these appeared to be where the cells had been visible in the first table. Rotating back then corrupted the initial orientation table with elements from the previous table.
I tried all of the applicable answers above, until I looked closer at the cellForRowAtIndexPath code:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
I understand cell re-use is a great idea and all, but I really didn't need to retain (as in preserve) any cells and wanted them all bright, spangly and new after each rotation.
EDIT: In my own app I'll have maybe 20-30 rows maximum, as I personally don't like hugely long tables. If there were going to be lots of rows returned for a particular query I'd have some filters available to the user to help them sort out which rows they wanted. If you're going to have loads of rows displayed, then dequeuing them may cause you a performance impact that you don't want.
All I did was comment out the if and the following bracket, and my table cells renewed exactly as I wanted them to:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
//}
Apologies for the waffle, and the late answer to an old question.
Ben.
Waffles and cream, or syrup.
You can use this simple line on the shouldAutorotateToInterfaceOrientation method :
self.view.autoresizesSubviews = YES;
For me it works always successfully
I am working on an application in which uitableviewcells of uitableview are customized which contains a textfied(contains managedobject/core data), label((contains managedobject), buttons so all are customized. i am creating uitableview dynamically using sqlite.
My tablview UI http://grab.by/FiJ
Similarly uitableview view contains more than 15 cells. Cells are displayed perfectly.
but i am facing problem with the memory occupied by cells.
And if i tab on next/prev the same table view is getting reloaded with new cells(also new data) using core data. But in this case previous cells(memory of cells) should be deallocted. But memory is getting increase and increase continuously while navigating to other views(using prev/next button).
Strange thing is,
When i scroll tablview continuously up and down cells are created and created, and memory is increasing gradually.
Thanks.
I think that
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
works incorrectly.
Check that your cells have same cell Identifier (You can specify it in IB) that you use in code:
static NSString *cellIdentifier = #"myIdentifier";
MyCell* cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[MyCell alloc] init] autorelease];
}
cell.text = #"Yahoo!";
...
I think that this is your problem.
The following method loads data from an array into custom cells of UITableView. The data is loaded in correctly. However, when I scroll down data in the above cells (the cells not visible) are changed to seemingly random elements in the array.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
cellComments=nil;
cellComments=(FullCommentCell *)[tableView dequeueReusableCellWithIdentifier:FullCommentCell_ID];
if(cellComments==nil)
{
[[NSBundle mainBundle]loadNibNamed:#"FullCommentCell" owner:self options:nil];
}
NSString *row = [NSString stringWithFormat:#"#%i",indexPath.row+1];
[cellComments loadFullComments:[latestFMLComments objectAtIndex:(indexPath.row+1)] withCommentNumber:row];
//cellComments.userInteractionEnabled=NO;
return cellComments;
}
I also have the following method that handles when i click on a cell. When the data of a cell changes to some random element in the array - if i click on the cell (which calls the method below) the data in the cell is changed to the right data.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self tableView:tableView cellForRowAtIndexPath:indexPath];
}
Any ideas why this is happening??
You are using cached cells, the odds are that you are not resetting the subvview elements of your cell correctly in loadFullComments.
Elfred has the right idea. When using the
cellComments=(FullCommentCell *)[tableView dequeueReusableCellWithIdentifier:FullCommentCell_ID];
Method call, you need to ensure that any subviews/properties such as labels, images, text, etc. are set every time on that cell. You can't assume that you'll be getting a "fresh" cell with no contents.
For example, when displaying the cell at index 17, the table view might dequeue the reusable cell previously at index 3. This cell will have the same properties as it did when it was in index 3, and it is your responsibility to reset them. (Eg. Changing the text from "I'm Cell 3" to "I'm Cell 17".)
I changed the UITextView to a multi-line UILabel and the everything works now. I am not sure why this makes a difference - but hey it works :)