temporarily prevent a UITableViewCell from being recycled - iphone

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"];

Related

Storyboard static cells: dequeueReusableCellWithIdentifier returns nil

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:)

How can I set the UITableViewCellAccessory for a chosen row? (iPhone/iPad)

I need a way of setting the UITableViewCellAccessory for any row. However the catch is that I need to be able to do it OUTSIDE of the UITableView delegate methods.
I have tried this, but it doesn't show up the accessory.
[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathWithIndex:1]] setAccessoryType:UITableViewCellAccessoryCheckmark];
If it makes any difference I created the UITableView in IB in a storyboard. The data is static, and I'm using a grouped table style with only one section.
Please can someone help me out?
If your table view is scrolled so the cell in question may sometimes scroll out of view and then back into view, you should manage the content of that cell only from within the UITableView method cellForRowAtIndexPath:. The reason is that when cells are redrawn, the tableview object calls this method to make sure that visible cells are properly rendered. (Cell that are not visible don't need to be rendered at all.)
That being said, this is where you should handle the cell content, even if the table view doesn't scroll the cell in question out of view. It wil lmake you life a lot easier if you follow this design pattern when working with table views.
Inside that method, you can test (using if statement, for example) the value of the indexPath.section and indexPath.row so that you can configure the specific cell the way you want it. This includes putting in the accessory.
Always use [NSIndexPath indexPathForRow:inSection:] when working with table views.
You can figure out what indexPath you need the checkmark on and then use something like this
UITableViewCell * cell = [tableView cellForRowAtIndexPath:someIndexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;

Weird UITableViewCell bug: accessory views change at random?

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:.

iPhone - having selected cell move to top of uitableview

I looked for this problem and I don't believe I could find an answer. I have a tableview of custom cells that when clicked, the selected cell pushes a new cell with information. I was wondering if anyone had an idea of how to push the selected cell to the top of the uitableview, or make it fill up the entire tableview.
My uitableview only takes up half of the screen, and I wish for when the cell is selected that it only take up that half of the screen, and the user is still able to scroll the cell (if that makes sense)?
Any suggestions would be appreciated, it was somewhat difficult to describe what I am looking for, so if anyone needs clarification please do not hesitate to ask me.
in cellForRowAtIndexPath, you need to place this in after you have created your cell
[tableView scrollToRowAtIndexPath:selectedCellIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
This did the trick for me with the custom height of the cells as well.
In my app I have a bunch of cells that expand to be larger when clicked; and only 1 cell can be expanded at a time. I basically keep track of the current selected indexPath, and then in heightForRowAtIndexPath: check to see if this is the selected sell; if it is I return a larger height.
Then, in didSelectRowAtIndexPath: I just set the current indexPath, and reload both the new cell and the previous one. This sounds similar to what you are looking for... would that work?
To move the selected cell to the top of the tableview, you could store the selected cell's index in an instance variable in didSelectRowAtIndexPath, and then call [tableView reloadData].
In your datasource's tableView:cellForRowAtIndexPath: method, you would ensure that the selected cell is returned for index 0 and all cells prior to the selected cell are returned for their index + 1.
If you are looking to move the cell, this is a base you could start from.
-(void)moveIndexPathToTop:(NSIndexPath *)pathToMove
{
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:pathToMove] withRowAnimation:UITableViewRowAnimationFade];
NSIndexPath *firstRow = [NSIndexPath indexPathForRow:0 inSection:0];
[myTable insertRowsAtIndexPaths:[NSArray arrayWithObject:firstRow] withRowAnimation:UITableViewRowAnimationFade];
}
This will move the row as far as the UITableView is conserned. Of course you'll need to match the new order in the data source also. (Like changing the order in your array of data)
Also, you'll probably want to scroll the table to the top when you do this.

iOS: ReusableCell changing my data on reuse

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 ?