I am having an issue with the resusable table cells. I only ever have 11 cells loading, where nearly all the content in them is downloaded, parsed and displayed correctly.
The problem that I am having is that I am shown a UIImageView square with a background colour unique to that row. And when the device is rotated or the table view is scrolled up or down (thus removing unused cells), the colours change to one of the others.
I am not completely sure how to stop this. Currently
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
is being called, and in this it detects the orientation, creates a Cell Identifier, creates the cell and adds things to the cell.
Any help would be great!
What are you using as a cell identifier? Use something unique, like
NSString *cellIdentifier = someObject.someProperty;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
where someObject.someProperty can be for example item.title
Hm, so this is how dequeuing works. Even though that conceptually there are 11 rows, actually iOS allows you to cheat and just reuse a few of them, so it works a lot more faster.
You just have to remember that if you asked the table view for a dequeued cell, you still have to reconfigure it so it shows correct data.
If your issue is because that your cells actually does internal processing… don’t do that. Move that piece of code elsewhere. ;)
To create tableviewCells you need to use a unique cell Identifier, but all cells in that tableview need the SAME identifier so:
static NSString *cellIdentifier = #"YOURINDETIFIER";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
Now you would setup your cells properties.
If you are using a custom cell, be sure to reconfigure ALL subviews otherwise you will get artefacts from previously used cells.
Can you post what you are doing in cellForRowAtIndexPath ?
Related
I am doing something very simple --- created a uicollectionview horizontally, add a button, label it with the indexpath.row (or indexpath.item, doesn't seem to matter) , which should show as
"0,1,2,3,4,5,6,7,8...."
but as soon as I scrolled back and forth, the numbers are completely out of wack, like
"5,11,4,6,2,3,7"
and the order keeps changing if I scroll.
I am thinking it has something to do with cell reuse, but I don't know how NOT to reuse cell. I never have this problem before I upgrade my Xcode to get the SDK 6.1....is this a bug or am I really doing this wrong??? How to solve it? Please help!
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"textbutton" forIndexPath:indexPath];
....}
You have to re-set the content cell after it was reused. You can't rely on the content of a reused cell. You are only allowed to reuse the layout of a view. Not its content.
Using storyboard, static cells, in cellForRowAtIndexPath: the line
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
always returns nil.
I have checked the following:
Identifier of the cell is correctly set in IB/Storyboard and I use the same identifier in code. I verified this many times.
I have not instantiated the view controller elsewhere (which was the problem in this stackoverflow question).
My view controller is a subclass of UITableViewController, of course, wrapped into an ad hoc navigation controller in storyboard. Suspecting that my view controller somehow does not know about the cell identifiers defined in storyboard because it might be another instance, here is the code the "instantiates" it. In prepareForSegue:, I use
CustomViewController *vc = [[[segue destinationViewController]
viewControllers] objectAtIndex:0];
Other customizations of the view controller done here (setting properties etc.) works fine.
I am using static cells because the number of sections and rows does not change, and each cell contains static text (and other controls or text fields to be edited).
It seems to me this is a very common task (customize static cells from storyboard in the view controller's datasource methods). What am I doing wrong?
With static content in a table view, you do not implement any of the datasource methods (including tableView:cellForRowAtIndexPath:, so you would never dequeue the cells. There is no dequeuing for static content (that you can get involved in, anyway).
If you want to get a pointer to a particular cell:
get it from the table view using cellForRowAtIndexPath::
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
have an outlet to the specific cell and customise it directly.
Iterate through the cells and check the reuseIdentifier property to get the cell you are interested in.
Any of these things can be done in viewWillAppear or similar.
If you want to have completely different content in your cells to that found on the storyboard then static cells probably aren't the right choice. You should use dynamic prototypes (note you can have multiple prototypes in the storyboard) with the traditional data source methods instead.
You can still use dataSource/delegate methods of static UITableView, you just don't have to create new cells.
If you want to modify cells with dataSource methods, inside cellForRowAtIndexPath: :
UITableViewCell * cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
and then start modifying cell.
The solution was to use prototype cells rather than static cells. I am still giving the check to #jrturton as he was the first who got me the idea.
Another interesting error I just solved: with prototype cells of type "Custom", if you try to fill cell.textLabel with text, it will just automatically work, but all your other subviews behave very strangely. I just use my custom label now, and everything works fine.
Cheers, thanks everyone for helping out.
Different to the answer above,
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
will not work. But the mentioned method to create an outlet to the cell itself is working.
It is also possible to place views such as UIButtons or UITextFields on the cell and have outlets for those as well.
Both methods can also be used in combination. E.g. set the cell.textLabel.text for a particular cell and have another control which will be accessed from the controls outlet.
In the storyboard, the Static Cells CAN'T implement the Methods in the <UITableViewDataSource> protocol.
So you could use the methods which ones are include in <UITableViewDelegate>.
I know this is a very old question, but the better way to handle this is to use tableView(_:willDisplay:forRowAt:)
I want two custom (i.e. subclassed) UIViews in a subclassed UITableViewCell as shown in the below picture. The two UIViews are the same subclass.
Both the custom UIViews and the TableViewCell have associated xib's.
I would appreciate some advice on the best way to go about this. I load the TableViewCell this way.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:NULL];
// CustomCell is an IBOutlet connected to the above nib
cell = BLCustomCell;
}
// configure the cell
}
I want to set outlets in the Custom Views to easily display data from my data model. Do I need a view controller for the Custom Views? I'm having trouble getting the nib to load for the Custom Views. (And yes, I realize my code above does not address this issue.) How do I get it to load? Does the TableView's controller need outlets to the Custom View objects?
Thanks!
The simplest way to handle complex UITableViewCells is to create a subclass of UITableViewCell, with its own IBOutlets that connect to the subviews, then just set properties of your custom cell in cellForRowAtIndexPath:. There are various other approaches, but this one seems to break down the problem reasonably well, and expands to handle more complex situations.
Take a look at the iOS Recipes book by Matt Drance, it cover this area well.
So, I have a UITableView set up to display all the system languages, where the user can select one. Upon selection, the cell sets its accessory view to a check mark and sets all the other cells' accessory views to none. But for some reason, when one cell is selected, another cell will randomly get selected as well.
For example: Here I select English UK.
But then when I scroll down (here showing the bottom of the list), English US seems to have automatically selected itself.
And then when I scroll back up to the top, English UK has magically deselected itself, but Spanish has been checked somehow.
Here is the entire source of the ViewController subclass: http://pastebin.com/EYNS9ahk
I also tried implementing a delegate method to check if the tableview is inadvertently selected at any point, but that's not the problem. Any ideas as to what is going on here?
You code seem me fine and well written, I guess the issue could be with the cell reusability,
Try with do the below changes,
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell_%d",index.row];
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
let me know if you are still getting the same issue.
Since the cell is reusable, when scrolling, the cells outside the view port will enqueue, and the -tableView:cellForRowAtIndexPath: will dequeue the enqueued cells for reusing, the cells' property remain the status on enqueuing, so you need to set to new status.
to fix your problem, you need to remember the selected cell's indexPath or other things can identity the cell, and set the accessoryType for it in -tableView:cellForRowAtIndexPath:, do not rely on the -tableView:didSelectRowAtIndexPath:.
My UITableViewCells can accept input of data. While working in one cell, it would be perfectly natural for the user to want to scroll up, check something in another cell, then return to the first cell and continue the data entry.
The problem is that this will frequently cause the UITableView to recycle the cell, which wreaks havoc in my program.
Is there any way to temporarily tell iOS not to recycle the cell?
Once the data entry is done, I am fine if it is recycled.
I should add that the data entry uses a custom keyboard. So first responder status is not an issue.
Give a different cellIdentifier to the cells that are significantly different. If the cell at the bottom has its own identifier, then when the user scrolls to the bottom, it won't recycle your cell from the top. However, this will still preserve your cell at the top in the reuse queue, so that when you scroll back to the top, you won't need to recreate it.
There's no way how to prevent cell recycling if you do use dequeueReusableCellWithIdentifier: method of UITableView in a common way.
If you would like to do this, you should implement it on your own in UITableView's data source protocol method - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath.
As Chiefly Izzy said, it's not a common way to work. And going against the way the lists work may cause problems. If you want to reduce your problem, keep the content of your cell in memory and use this saved content to refill the cell when willDisplayCell is called instead of rebuilding the whole thing from start each time.
Gendolkari's solution gave me an idea for temporarily removing a cell from the dequeue cycle. It does involve setting a read-only value using KVC, so it's not for the truly hack-adverse.
When you first want to remove the cell from the cycle, change the reuseIdentifier for that particular cell. We want to be able to find the cell later so rename it something that we can index against. I found [NSIndexPath description] to be useful here. The reuseIdentifier is a read-only property, but this is Objective-C, and we have KVC:
[cell setValue:[[self.tableView indexPathForCell:cell] description] forKey:#"reuseIdentifier"];
Now whenever we reuse a cell in the cellForRowAtIndexPath:
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
we won't get back this particular cell because we have changed the reuseIdentifier.
However, if we scroll down then scroll back up, we probably want to display the original cell that we removed from the dequeue cycle. To do this when we are reusing a cell first attempt to dequeue a cell based on indexPath, and if we can't find that attempt to dequeue a cell simply based on the basic cell identifier string:
UITableViewCell *cell;
cell = [self.tableView dequeueReusableCellWithIdentifier:[indexPath description]];
if (!cell) cell = [self.tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
When you want to put that cell back into the dequeue cycle, simply change the reuseIdentifier back to the original value:
[cell setValue:#"cellIdentifier" forKey:#"reuseIdentifier"];