I'm finding that if you set a table view into editing mode, upon scrolling the table after deleting a row the cell edit control (the red minus sign on the left) is in a random state (turned vertical or horizontal) on the rest of the cells as you scroll the table. Presumably because I'm reusing cells like this:
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
How can I force the edit control to be in the correct state for each cell? It should always be in the default horizontal state unless I tap it to delete a cell.
EDIT: Here's the cell code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"WhateverIdentifier";
MyCell *cell = nil;
//This IF statement fixes the problem, but then I'm not reusing the cells
if (!tableView.isEditing) {
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
}
if (!cell) {
cell = [[[NSBundle mainBundle] loadNibNamed:#"MyCell" owner:self options:nil] objectAtIndex:0];
}
//customize cell
return cell;
}
Are you calling [super prepareForReuse] in the method prepareForReuse of your custom cell?
That resolved my problem.
I just checked in a UITableView I had handy, and I don't see that problem. I'm using dequeueReusableCellWithIdentifier: as you are (without the if statement, of course). Are you doing something special with swiping, or deleting multiple rows or something?
(I'd make this a comment but can't yet. I'll delete it once you've resolved your problem.)
Related
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"MyCell";
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil)
cell = [[MyCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
Movie *movie = [self.movies objectAtIndex:indexPath.row];
cell.title.text = movie.title;
cell.subtitle.text = movie.subtitle;
cell.subtitle.numberOfLines = 0;
[cell.subtitle sizeToFit];
return cell;
}
I am calling reloadData from two places. One is from the end of a loadInitialData function, which is called from viewDidLoad.
A second one is being called from viewDidAppear, although this is inconsequential to my problem, because it existed before it and exists without it.
I initially load 3 rows of sample data, with titles and subtitles. Now what happens is my subtitle text is vertically centered when this window first launches. If I grab the table and scroll is high up, all of a sudden my [cell.subtitle sizeToFit] goes into action, and my text goes to the top vertically, which is desired.
So my issue is... why is the text vertically centered from the beginning? reloadData doesn't work either. When I return from adding a new row, all rows but the newly added row are vertically aligned to top as they should. The new row is incorrectly vertically centered.
Why doesn't this work? Everything seems good. New data is added etc. Via NSLog statements, I have verified numberOfRowsInSection is immediately called after reloadData is called.
So why does the aligning of the text vertically to the top not work?
Thanks!
This is probably because the UITableViewCell has not yet been layed out and so it does not have a size yet. Try doing the sizeToFit in this UITableViewDelegate method
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
I am not sure this will work, but it worth trying.
Add [cell setNeedsLayout]; before you return the cell so it will layout the cell before presentation.
So I've created a TableView as below, but I have quite an annoying problem.
When I come to this VC, I click Choose User, and I selected user Atoshum.
When I scroll down, this top cell goes off screen as I scroll through the bottom cells.
When I scroll back up, the cell has reverted to a default (or occasionally, takes the value of another cell).
I make the cells as such.
static NSString *CellIdentifier = #"Cell";
DMSDrugInstanceCell *Cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!Cell) {
Cell = [[DMSDrugInstanceCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Cell.chooseUserButton.tag = indexPath.row;
[Cell.chooseUserButton addTarget:self
action:#selector(performSegue:)
forControlEvents:UIControlEventTouchUpInside];
return Cell;
In the cell creation you dont ever set any values. All you do is set the tag and then add an event target. If you want it to keep the choose, you need to store/save that choice when it is made and then in the cell create, set it based on that saved value.
you are reusing table cell. So every time your cell reload then you need to set value in cell.
in cellForRowAtIndexPath method , you need to set value in cell according to index path.
This is because the UITableView do not create the new cells for total number of elements. Rather it re-uses the cells which are off the visibility. Hence you feel that your data is reset or getting reflected on some other cell.
The best approach is to store all your data in some Array (let it be tagDataArray) and then set your cells as
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
DMSDrugInstanceCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[DMSDrugInstanceCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Cell.chooseUserButton.tag = [tagDataArray objectAtIndex:indexPath.row];
...
...
return Cell;
}
I'm using a custom UITableViewCell subclass, called MessageCell.
I initialise the cell like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MessageCell *cell = [tableView dequeueReusableCellWithIdentifier:#"messageCell"];
// more code
return cell;
}
In each custom cell I have a checkbox used to mark this cell. When a cell is marked it is being added to an array, and vice versa.
While this works in terms of data, the UI is not reflecting it as it should. What happens is that in addition to the marked cell, other cells are being marked.
I assume this behaviour stems from cell reusability, i.e when I mark a cell as selected it has a common pointer with all future cells in this screen position.
For this reason, I want to use "normal" non-reusable cells.
So I tried:
MessageCell *cell = [[MessageCell alloc] init];
OR:
MessageCell *cell = [[MessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
But both of them resulted in displaying empty cells.
You still want to reuse cells! Just check your data and set the checkbox to checked or unchecked in your cellForRowAtIndexPath:
MessageCell *cell = [tableView dequeueReusableCellWithIdentifier: #"messageCell"];
if (condition cell should be checked) //set the cells checkbox to checked
else //set the cells checkbox to unchecked
Or some approach like that.
PS on re-reading your question:
do you really need a reference to a cell in your array? Can't you for example store the indexPath and use cellForRowAtIndexPath: if you need to make a cell out of it?
I have a UITable which shows the 10 most recent images from a web service. Each row has its own image. When the UITable first loads in the viewcontroller it doesnt show the first 4 images in the rows (screen is blank). If I scroll down the last 6 images appear....then if I scroll back the first 4 images that were NOT there originally DO appear and everything looks the way I wanted it to initially. My guess is its something do with the way cells are reused.
Here is my tableView code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
}
[cell.contentView.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
[cell.contentView addSubview:[self.photoList objectAtIndex:indexPath.row]];
return cell;
}
What solved my issue was adding the following line to my method that deals with streaming the photos from the Web API:
[_tableView reloadData];
I noticed that if I moved the Table View Cell up or down in the its Table View at the Storyboard that changed the position of the invisible row as well. So I solved the problem by setting the Table View Cell as hidden.
In my UITableView, I recently changed the structure of the cell from formerly just putting UILabels in the contentView of the cell, to adding two UIViews (CellFront and CellBack, on top of one another) into the contentView (so I can achieve a sliding effect by sliding the top one off and revealing the lower one) and adding the UILabels to the top UIView.
However, now, for whatever reason, the cells never get init'd and as a result my UITableView is full of blank cells.
My cell gets created as follows (ArticleCell is a subclass of UITableViewCell):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = nil;
ArticleInfo *articleInfo = [self.fetchedResultsController objectAtIndexPath:indexPath];
// Checks if user simply added a body of text (not from a source or URL)
if ([articleInfo.isUserAddedText isEqualToNumber:#(YES)]) {
CellIdentifier = #"BasicArticleCell";
}
else {
CellIdentifier = #"FullArticleCell";
}
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[ArticleCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// If the user simply added a body of text, only articlePreview and progress has to be set
cell.preview = articleInfo.preview;
// If it's from a URL or a source, set title and URL as well
if ([articleInfo.isUserAddedText isEqualToNumber:#(NO)]) {
cell.title = articleInfo.title;
cell.URL = articleInfo.url;
}
return cell;
}
But I set a breakpoint on the initWithStyle method above within the if statement and it never gets called:
What would cause this? I'm deleting the app and building it from scratch every time, so data is definitely being added to the UITableView, but all the cells are blank. And I can tell a bunch of cells are being added as I have disclosure indicators on all of them, and the table view just gets filled with empty cells with the indicators only.
What am I doing wrong?
try
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
instead of
ArticleCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
the first one is the old standard way. It will not create a cell for you. While with the second a cell will be created form the storyboard. So if you use storyboards you should use indeed the method you are using now, but it will never go info the if branch, as the cell will never be nil.
when instantiating form storyboard, initWithStyle:reuseIdentifier: is never called. Either set everything up in -initWithCoder: or -layoutSubviews