I have a problem displaying UITableView: Some cells are empty, and content in cells becomes visible only after scrolling (if empty cell scrolls out of the screen and then goes back). I Can't understand what might be the problem.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self updateFilesList];
[tableView reloadData];
}
- (void) viewDidAppear:(BOOL)animated
{
animated = YES;
[self updateFilesList];
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[self.filesList retain];
NSString *title = [self.filesList objectAtIndex:indexPath.row];
title = [title stringByDeletingPathExtension];
title = [title lastPathComponent];
if (title.length >33) {
title = [title substringFromIndex:33];
}
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
[cell.imageView setImage:[UIImage imageNamed:#"oie_png-1.png"]];
cell.textLabel.text = title;
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
return cell;
}
Thank you in advance for your suggestions!
Well, you have the cell customization code that happens before you create the cell.
Change it like this :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
[self.filesList retain];
NSString *title = [self.filesList objectAtIndex:indexPath.row];
title = [title stringByDeletingPathExtension];
title = [title lastPathComponent];
if (title.length >33) {
title = [title substringFromIndex:33];
}
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// This part creates the cell for the firs time
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// This part customizes the cells
[cell.imageView setImage:[UIImage imageNamed:#"oie_png-1.png"]];
cell.textLabel.text = title;
return cell;
}
The problem is that you make
[cell.imageView setImage:[UIImage imageNamed:#"oie_png-1.png"]];
cell.textLabel.text = title;
before
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
change the order. What is happening is that the first time you execute the table View you never do the title before the alloc. When you reuse cell it works because the cell != nil
You need to put these lines
[cell.imageView setImage:[UIImage imageNamed:#"oie_png-1.png"]];
cell.textLabel.text = title;
after the if(){...} condition.
In the first pass cells are nil. Put those lines before the if does nothing. This is why you see empty cells.
A simple question
Why do you call [self.filesList retain] ?
Hope it helps.
Related
Here I am trying to do dynamic UITableViewCell with UIPickerView.
Step 1:
In Custom cell, I took 1 label and 1 UITextField.
Step 2:
used downpickerview library for data displaying and data fetching.
Step 3:
using below code I can able to select data, but after that, if I scroll UITableView data will be miss placing.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"Cell";
customCell *cell1=[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell1 == nil)
{
cell1 = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
cell1.productTitleLabel.text =[arrProductTitle objectAtIndex:indexPath.row];
self.downPicker = [[DownPicker alloc] initWithTextField:cell1.productvalueTextfield withData:arrProductVal];
[self.downPicker addTarget:self action:#selector(measurementSelected:) forControlEvents:UIControlEventValueChanged];
[cell1.contentView addSubview:self.downPicker];
return cell1;
}
Please help me on this.
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"DETAILS";
_dict = [[NSMutableDictionary alloc]init];
arrProductTitle = [[NSMutableArray alloc]initWithObjects:#"title0",#"title1",#"title2",#"title3",#"title4",#"title5",#"title6",#"title7",#"title8",#"title9",#"title10",#"title11",#"title12",#"title13",#"title14",#"title15", nil];
arrProductVal = [[NSMutableArray alloc]initWithObjects:#"0",#"1",#"2",#"3",#"4",#"5",nil];}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return arrProductTitle.count-1;}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *MyIdentifier = #"Cell";
customCell *cell1=[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell1 == nil)
{
cell1 = [[customCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
cell1.productTitleLabel.text =[arrProductTitle objectAtIndex:indexPath.row];
NSLog(#"%#",_dict);
NSLog(#"%#",[NSString stringWithFormat:#"%ld",(long)indexPath.row]);
if (_dict[[NSString stringWithFormat:#"%ld",(long)indexPath.row]]) {
cell1.productvalueTextfield.text =[_dict valueForKey:[NSString stringWithFormat:#"%ld",(long)indexPath.row]];
}
else {
cell1.productvalueTextfield.text = #"";
self.downPicker = [[DownPicker alloc] initWithTextField:cell1.productvalueTextfield withData:arrProductVal];
self.downPicker.tag = indexPath.row;
[self.downPicker addTarget:self action:#selector(measurementSelected:) forControlEvents:UIControlEventValueChanged];
[cell1.contentView addSubview:self.downPicker];
}
return cell1;}
-(void)measurementSelected:(id)dp {
NSString* selectedValue = [dp text];
NSString* selectedIndex = [NSString stringWithFormat:#"%ld",(long)[dp tag]];
[_dict setValue:[dp text] forKey:selectedIndex];
NSLog(#"_dict: %#",_dict);
NSLog(#"SELECTED TAG:::::::%ld",[dp tag]);
NSLog(#"SELECTED VALUE:::::::%#",selectedValue);
NSLog(#"SELECTED INDEX VALUEEEEEEEEEEE:::::::%ld",[dp selectedIndex]);}
https://github.com/gvniosdev/Dynamic-UItableview-with-Picker-Selection
UITableViewCell values are misplacing because you haven't set the data for other cells, You need to store the values in an array and just update the values from there and it will work. :)
Something is not right with your code:
cell1.productTitleLabel.text =[arrProductTitle objectAtIndex:indexPath.row];
self.downPicker = [[DownPicker alloc] initWithTextField:cell1.productvalueTextfield withData:arrProductVal];
[self.downPicker addTarget:self action:#selector(measurementSelected:) forControlEvents:UIControlEventValueChanged];
[cell1.contentView addSubview:self.downPicker];
Bear in mind that in iOS, a tableView will reuse its cells. So when you scroll your UITableView, it will reuse the old cells which were created before, and
[cell1.contentView addSubview:self.downPicker]; Will be executed everytime a cell is reused, as a result, you will end up having many downPicker objects in one cell.
I have a problem with uiswitch in my UITableViewCell that whenever i change a switch value in a specific cell that belongs to a specific section. All other sections cells that have same inexPath.row change . Please help ?
Here is my code of cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//UISwitch *switchview = nil ;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
UISwitch *switchController = [[UISwitch alloc] initWithFrame:CGRectZero];
CGRect switchFrame = switchController.frame;
NSString *str = [[self.SwitchArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
NSLog(#"string in cell%#", str);
//set its x and y value, this you will have to determine how to space it on the left side
switchFrame.origin.x = 50.0f;
switchFrame.origin.y = 10.0f;
switchController.frame = switchFrame;
[switchController addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
[cell addSubview:switchController ];
[addSubview:switchController];
if ([[[self.SwitchArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row ]isEqualToString:#"ON"])
{
switchController.on=YES;
}
else
{
switchController.on=NO;
}
return cell;
}
And here is SwitchChangedEvent:
-(void)switchChanged:(UISwitch *)sender
{
UITableViewCell *cell = (UITableViewCell *)[sender superview];
NSIndexPath *index=[mainTableView indexPathForCell:cell];
if (sender.on)
{
[[self.SwitchArray objectAtIndex:index.section] replaceObjectAtIndex:index.row withObject:#"ON"];
NSString *word= [[self.mainArray objectAtIndex:index.section ] objectAtIndex:index.row];
}
else
{
//call the first array by section
[[self.SwitchArray objectAtIndex:index.section] replaceObjectAtIndex:index.row withObject:#"OFF"];
NSString *word= [[self.mainArray objectAtIndex:index.section ] objectAtIndex:index.row];
}
[padFactoids setObject:[NSKeyedArchiver archivedDataWithRootObject:SwitchArray] forKey:#"savedArray"];
[padFactoids synchronize];
}
You have two main problems:
This code is wrong:
[cell addSubview:switchController ];
Never add a subview to the cell; add it only to the cell's contentView.
You are adding the switch every time through cellForRowAtIndexPath:. But these cells are being reused. So some cells already have the switch. Thus you are adding many switches to some cells.
As already pointed out by others, you're adding multiple switches to each cell. FTFY:
UISwitch *switchController = nil;
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
switchController = [[UISwitch alloc] initWithFrame:CGRectZero];
switchController.tag = 'swch';
CGRect switchFrame = switchController.frame;
//set its x and y value, this you will have to determine how to space it on the left side
switchFrame.origin.x = 50.0f;
switchFrame.origin.y = 10.0f;
switchController.frame = switchFrame;
[switchController addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
[cell.contentView addSubview:switchController];
}
else
{
switchController = (UISwitch*)[cell.contentView viewWithTag: 'swch'];
NSAssert(switchController != nil, #"how?");
}
NSString *str = [[self.SwitchArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
NSLog(#"string in cell%#", str);
switchController.on = [str isEqualToString:#"ON"];
return cell;
If you look, I think you will find that you are building up multiple switches and on each cell.
As cells get reused, you add a new switch but do not remove the old one. I think this my be at least part of the cause of your problem.
UPDATE
I think I would handle this a bit differently than others. I think it will be easier for #AlanoudAldrees to remove the old switch.
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
else
{
for (UIView *view in cell.subviews)
if ([view isKindOfClass:[UISwitch class]])
[view removeFromSuperview];
}
My tableview works perfectly fine when not in editing mode. All cells show up as expected, but if I enter editing mode and scroll, the cells that are redrawn while in editing mode have the incorrect content. In my function that turns off editing, I reload the table data and it shows up correctly again.
Here the relevant code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
FieldItemDecrypted *theField = [decryptedArray objectAtIndex:indexPath.row];
// Configure the cell...
cell.textLabel.text = [[NSString alloc] initWithData:theField.field encoding:NSUTF8StringEncoding];
cell.detailTextLabel.text = [[NSString alloc] initWithData:theField.type encoding:NSUTF8StringEncoding];
return cell;
}
And my code for editing:
- (IBAction)editRows:(id)sender
{
if ([self.tableView isEditing])
{
[self.tableView setEditing:NO animated:YES];
[self.tableView reloadData];
}
else
{
[self.tableView setEditing:YES animated:YES];
}
}
Should look like this:
but looks like this after scrolling while editing:
You are initializing a UITableViewCell object first, then dequeuing from the table view? This is incorrect.
Try:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
I just tried this on a demo project and it behaves as expected.
I'm more familiar with this type of cell reuse:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:nil];
}
FieldItemDecrypted *theField = [decryptedArray objectAtIndex:indexPath.row];
// Configure the cell...
cell.textLabel.text = [[NSString alloc] initWithData:theField.field encoding:NSUTF8StringEncoding];
cell.detailTextLabel.text = [[NSString alloc] initWithData:theField.type encoding:NSUTF8StringEncoding];
return cell;
}
I'm adding two custom UILabel to a section of my UITableView in this way:
//in .h file:
NSArray *listaopzioni;
#property (nonatomic, retain) NSArray *listaopzioni;
//in .m file:
self.listaopzioni = [[NSArray arrayWithObjects:#"Strumenti",#"Help & Credits", nil] retain];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
if ([indexPath section]==0) {
cell.accessoryType = UITableViewCellAccessoryNone;
UILabel *slogan= [[UILabel alloc] initWithFrame:CGRectMake(0,0,cell.frame.size.width,cell.frame.size.height)];
slogan.text=[listaopzioni objectAtIndex:indexPath.row];
slogan.textAlignment=UITextAlignmentCenter;
slogan.font= [UIFont boldSystemFontOfSize:20];
slogan.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:slogan];
[slogan release];
}
}
All woks perfectly, but when I slide up and down the tableview (trying to cover the cells below the UINavigationBar) I get a strange effect: the text is overlapped just making each letter thicker.
What's wrong?
Method cellForRowAtIndexPath is called everytime the cell becomes visible.
That is why it creates labels everytime you scroll.
The solution is to put Label creation when you create the cell:
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
if ([indexPath section]==0) {
cell.accessoryType = UITableViewCellAccessoryNone;
UILabel *slogan= [[UILabel alloc] initWithFrame:CGRectMake(0,0,cell.frame.size.width,cell.frame.size.height)];
slogan.text=[listaopzioni objectAtIndex:indexPath.row];
slogan.textAlignment=UITextAlignmentCenter;
slogan.font= [UIFont boldSystemFontOfSize:20];
slogan.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:slogan];
[slogan release];
}
}
UITableViewCells (when used properly) get reused, which means after they've been created, they keep their created state, i.e. the label has been added to your cell. What you need to do is make use of this cell reuse to your advantage:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UILabel slogan;
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
slogan = [[UILabel alloc] initWithFrame:CGRectMake(0,0,cell.frame.size.width,cell.frame.size.height)];
slogan.tag = 2121; // Any unique-to-the-cell, positive integer
slogan.textAlignment=UITextAlignmentCenter;
slogan.font= [UIFont boldSystemFontOfSize:20];
slogan.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:slogan];
} else {
slogan = [cell viewWithTag:2121]; // Must match slogan.tag
}
if ([indexPath section]==0) {
cell.accessoryType = UITableViewCellAccessoryNone;
slogan.text=[listaopzioni objectAtIndex:indexPath.row];
}
}
I am trying to display the contents of an array onto a uitableview. It works fine if I assign the values individually but when I try to display them from an array xcode exits with nothing in the debugging report except (lldb). It compiles and runs great until I go to the page with the tableview. I've looked up the error and it seems to pertain to memory allocation but I am unable to manually release anything due to having to have ARC enabled for some JSON classes to operate. Can anyone take a look and see what possibly might be the issue.
- (void)viewDidLoad
{
[super viewDidLoad];
theArray = [[NSArray alloc] initWithObjects:#"1","2","3","4",nil];
// Do any additional setup after loading the view.
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [theArray count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Set up the cell...
self.recentSearchesTable.backgroundColor =[UIColor clearColor];
self.recentSearchesTable.separatorColor = [UIColor clearColor];
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:15];
cell.textLabel.text = [theArray objectAtIndex:indexPath.row];
cell.contentView.backgroundColor = [UIColor clearColor];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// open a alert with an OK and cancel button
NSString *alertString = [NSString stringWithFormat:#"Clicked on row #%d", [indexPath row]];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:alertString message:#"" delegate:self cancelButtonTitle:#"Done" otherButtonTitles:nil];
[alert show];
}
In viewDidLoad your array contains non-object items, change it too:
theArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"4",nil];
Note the # before each item in the array, indicating that they're NSString literals (and not c strings).
You did two mistake here first you need to initialize array in this way.
theArray = [[NSArray alloc] initWithObjects:#"1",#"2",#"3",#"4",nil];
and another mistake is you need to initialize cell when it is nil
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
if you want whole code then like this way you need to implement.
- (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] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Set up the cell...
cell.textLabel.text = [theArray objectAtIndex:indexPath.row];
cell.contentView.backgroundColor = [UIColor clearColor];
return cell;
}
First of all you do not need to disable ARC for your project due to an external class.You can exclude these classes by setting -fno-objc-arc flag.
Back to your answer
You should alloc a new cell if it can be reeused from a previous one.You should alloc you new cell if dequeueReusableCellWithIdentifier:CellIdentifier does not return a cell.
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:Cellidentifier] autorelease];
}
Apart from that when you init your datasource array in viewDidLoad you need to add # before each element to indicate NSString.