The leak instrument points to "cell.textLabel.text = str;" as a memory leak. I am not sure why since I autoreleased the cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
// Try to retrieve from the table view a now-unused cell with the given identifier.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
// If no cell is available, create a new one using the given identifier.
if (cell == nil) {
// Use the default cell style.
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}
// Set up the cell.
NSString *str = [array objectAtIndex:indexPath.row];
cell.textLabel.text = str;
return cell;
}
you might not have released the array object that you use for getting the Strings.Also , try casting the value after extracting from array as
str= [NSString stringWithFormat:"%#",(NSString *)[array objectAtIndex:indexPath.row]];
Related
My cell.textLabel.text sometimes returns an empty string. Is there a way I can skip this cell and put my next text label right after my last non empty cell.textLabel.text? So there should no empty labels.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
ResultModel *resultModel = [self.array objectAtIndex:indexPath.row];
cell.textLabel.text = resultModel.resultString;
return cell;
}
cell.textLabel takes the whole content view's size.width so you calculate the length of the string and manipulate next label accordingly
Try this code.
if([resultModel.resultString isEqualToString:#""])
{
// Display the label
cell.textLabel.text = resultModel.resultString;
}
else
{
//result string is Null do nothing here.
}
Why don't you just get instances which have value in "resultString" property?
NSArray *dataWithResultString = [call the custom method to get instances with no empty value];
ResultModel *resultModel = [dataWithResultString objectAtIndex:indexPath.row];
This you should handle in the datasource array itself...
Populate a new array with values having the resultModel.resultString and use this array as the datasource for tables
if i understand you correctly you want the cell to be displayed only if there is some non empty string in resultModel.resultString, right?
For that you better handle the datasource array itself. If that's hard, you can do this.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
ResultModel *resultModel = [self.array objectAtIndex:indexPath.row];
if([resultModel.resultString isEqualToString:#""])
return 0.0;
else
return 50;//or whatever
}
But its better handling the datasource itself.
If I'm reading you right, the resulted array might return an empty string and if so, you don't want to show those rows right?
Ideally, you should've add another parameter to your query to exclude the empty resultString. However, if you want a hacky way, declare (int)padRows and set it to 0 in viewDidLoad;
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
ResultModel *resultModel = [self.array objectAtIndex:indexPath.row];
while([resultModel.resultString isEqualToString:#""]) {
_padRows += 1;
resultModel = [self.array objectAtIndex:(indexPath.row+_padRows)];
}
cell.textLabel.text = resultModel.resultString;
return cell;
}
Code not tested.
Apologies if this comes across as a beginner's question. I'm trying to populate a UITableView with sections and custom cell formatting.
I've created a customCell in ViewControl.xib which sits along the main view and looks like this:
customCell image
I have a dictionary to load up the values using a method in another class, depending on which row it's at. If it's in Row 1, load details for item 1 etc.
This is the code I'm using:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"customCell"];
// assigns current row's labels
NSArray * customCellText = [Model cellText:indexPath.row];
dinnerItem.text = customCellText[0];
dinnerDescription.text = customCellText[1];
dinnerTime.text = customCellText[2];
cell = customCell;
return cell;
}
And this is currently what's being generated:
iPhone simulator screenshot
The issues I have:
It's not populating all rows, only the last one.
I can only seem to click on the row which is populated, and even then
it stays selected as opposed to 'clicking on it'.
If I drag it up or down quickly it crashes.
I presume it has to do with the way it's redrawing/populating cells?
Thanks in advance!
SineTwo
EDIT, ADDED MORE CODE FOR CLARIFICATION:
ViewController.m
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [Model countKeys];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [Model rowsInSection:section];
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
// slightly crap code, this is initiated in viewDidLoad and is an array created by a method in Model.m. Only looks for keys and returns an array.
return headerKeys[section];
}
Model.m
+(NSArray *)headerKeys
{
NSArray *headerKeys = [[NSArray alloc] init];
headerKeys = [timerDictionary allKeys];
NSLog(#"All keys: %#", headerKeys);
return headerKeys;
}
+(NSArray *)customCellText
{
NSArray *customCellText = [[NSArray alloc]initWithObjects: #"dinnerItemText", #"dinnerDescriptionText", #"01:00", nil];
return customCellText;
}
+(NSInteger)rowsInSection:(NSInteger)sectionNumber
{
NSArray *keyContent = [[NSArray alloc] init];
keyContent = [timerDictionary objectForKey:dictionaryKeys[sectionNumber]];
NSLog(#"current section[%i]: %i", sectionNumber, [keyContent count]);
return [keyContent count];
}
+(NSArray *)cellText:(NSInteger)rowNumber
{
// display all dictionary keys, dictionaryKeys[x] will give back the specific category
dictionaryKeys = [timerDictionary allKeys];
// displays contents of first key in dictionary
NSArray *keyContent = [[NSArray alloc] init];
keyContent = [timerDictionary objectForKey:dictionaryKeys[0]];
// creates an array with all items within the selected key
NSArray *keyItems = [[NSArray alloc] initWithArray:keyContent[rowNumber]];
return keyItems;
}
I can say the following:
if you only do that:
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"customCell"];
prior to iOS 5 this doesn't guarantee you getting back a cell. Prior to iOS5 you have to do the following:
NSString *cellIdentifier = [NSString stringWithFormat:#"I%d-%d", indexPath.section, indexPath.row];
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"customCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
// assigns current row's labels
NSArray * customCellText = [Model cellText:indexPath.row];
dinnerItem.text = customCellText[0];
dinnerDescription.text = customCellText[1];
dinnerTime.text = customCellText[2];
cell = customCell;
return cell;
}
If you are on iOS5/6 these lines are not needed anymore:
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
But you have to use the following methods in the tableView setup:
registerClass:forCellReuseIdentifier: (available in iOS6 and later)
registerNib:forCellReuseIdentifier: (available in iOS5 and later)
Just hoping to solve your problem:
Don't forget the lines [...](cell == nil)[...]
First, change your method to this! You need to check if the cell is nil:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"customCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// assigns current row's labels
NSArray * customCellText = [Model cellText:indexPath.row];
dinnerItem.text = customCellText[0];
dinnerDescription.text = customCellText[1];
dinnerTime.text = customCellText[2];
cell = customCell;
}
return cell;
}
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.
I've checked a few other questions but still can't figure out why this is happening. All I know is after using the description code from this question the cell's value is null. I've set the delegate and datasource of the table correctly and I can't see where this is going wrong.
If I set the return value of the following method to 0, no error occurs because the cellForRowAtIndexPath method doesn't really take place. So if I set it as 1 (as it should be) it'll then throw the error. I've synthesized the NSArray and checked that its populated (although that shouldn't matter I guess) and basically what is meant to happen is I press a button that searches and then places the results in the table. So before this the tableview is empty.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
Here's the cellForRowAtIndexPath method itself. I've tried just using the basic template method too, which still throws the same error.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
NSDictionary *aResult = [results objectAtIndex:[indexPath row]];
NSLog(#"RESULTS IN TABLE %i", [results count ]);
id key = [results objectAtIndex:[indexPath row]];
resultTitle.text=#"Hello";
NSURL *url = [NSURL URLWithString:[aResult objectForKey:#"url"]];
NSData *data = [NSData dataWithContentsOfURL:url];
cell.imageView.image = [UIImage imageWithData:data];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[theTableView reloadData];
return cell;
}
I've no idea what is wrong. I've used tableviews before and have compared this project to others to see if I've missed something, but I can't see any real differences.
you are not initisialing the cell if nil is returned from the dequeue method
change
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
to
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
You do not have a method to return the number of rows in section 0 - this is a required data source method (in this case).
I have a UITableView that is put onto a view based app. it is wired up properly because I can initially send it data. The problem is clearing the table. I can clear the NSMutableArray but the data still shows up in the table.
-(void)logout {
NSLog(#"LOGOUT");
[friends removeAllObjects];
NSLog(#"%#", friends);
[tableView reloadData];
}
and here is the cell for row at index path:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithFrame:CGRectZero];
}
// Set up the cell...
NSString *cellValue = [friends objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
Create a dedicated reference
#property(nonatomic, retain) IBOutlet UITableView *tableViewReference;
Reload that reference in the logout method.