I just try to create an app for iPhone and iPad. For this i created a tableview that creates cells expecting the json that will be received when the app is loading. Inside the method
tableView:cellForRowAtIndexPath:
I initiate the cell that will be used for the iPhone and I set the specific values from the json. The cell I use is a custom Cell i created with a new object and NIB-File. The code looks like this:
static NSString *CellIdentifier = #"tvChannelIdentifier";
tvChannelCell *cell = (tvChannelCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"tvChannelCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
....
Now that I try to make the app work on the iPad too, I created a new custom cell, also with a new object and a NIB-File. The names of the variables are the same as in the customIPhoneCell so that I don't have to change the whole code. I just inserted a switch inside the tableView:cellForRowAtIndexPath: so that i show the right cell but it can't be accessed by the code:
static NSString *CellIdentifier = #"tvChannelCell";
static NSString *IPadCellIdentifier = #"tvChannelIPadCell";
//determination which device ---> choose cell
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone){
tvChannelCell *cell = (tvChannelCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}else{
tvChannelIPadCell = (tvChannelIPadCell *) [tableView dequeueReusableCellWithIdentifier:IPadCellIdentifier];
}
I tried to define the cell objects at the top of the class but then i can't use the same variable name. I also tried to choose the NIB file of the iPad to check if it'll be displayed but nothing happens. And I tried to set an UITableviewcell at the top,
UITableViewCell *cell
and set the specific cell type inside my switch in cellForRowAtIndexPath but then the variables can't be accessed.
So my question is, is it possible to do this switch inside the tableview method or do i have to code to several parts for each device where each celltype has its own variablename?
What I do:
I create only one UITableViewCell subclass with 2 xibs, one xib for iPad and another for iPhone.
tvChannelCell *cell = (tvChannelCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone){
nib = [[NSBundle mainBundle] loadNibNamed:#"tvChannelCell_iPhone" owner:self options:nil];
}else{
nib = [[NSBundle mainBundle] loadNibNamed:#"tvChannelCell_iPad" owner:self options:nil];
}
cell = [nib objectAtIndex:0];
}
this line of code useful for differenciating iphone and ipad,
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
tvChannelCell *cell = (tvChannelCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}else{
cell = (tvChannelIPadCell *) [tableView dequeueReusableCellWithIdentifier:IPadCellIdentifier];
}
It's quite simple, why don't you create another nib for iPad, and just access it when device is iPad.
You all code will remain same, i.e. you don't need to compare iPad like anywhere
Like:
if (cell == nil) {
NSArray *nib;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone){
nib = [[NSBundle mainBundle] loadNibNamed:#"tvChannelCell-iPad" owner:self options:nil];}
else{
nib = [[NSBundle mainBundle] loadNibNamed:#"tvChannelCell" owner:self options:nil];}
cell = [nib objectAtIndex:0];
}
Related
I am getting leak in following code. Leak percentage # the end of the line. Can anyone tell me what is the problem.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
coustomMessage *cell = (coustomMessage *)[tableView dequeueReusableCellWithIdentifier:#"coustomMessage"];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"coustomMessage" owner:self options:nil]; (93.1%)
cell = [topLevelObjects objectAtIndex:0];
}
cell.nameLable.text = [self.nameArray objectAtIndex:indexPath.row]; (3.4%)
cell.messageStatusLable.text = [[self.endPointCountArray objectAtIndex:indexPath.row] stringValue]; (3.4%)
return cell;}
are you sure you setted the "identifier" property in your XIB file with the same name you use in your code (with: dequeueReusableCellWithIdentifier:#"coustomMessage")?
Superb! I had exactly the same issue I misspelled the identifier in the XIB and so lots of leaks as the cell we being recreated not re-used. Thanks!
I have a TableView loading a custom cell and loading the data from a JSON string on a server.
The JSON string is parsed to an array of 14 IDs (id_array).
If cell==nil, then I'm using [id_array objectAtIndex:indexPath.row] to get an ID and fetch some more info about for that row from a server, and set up the cell's labels and image.
When running the app, the UITableView is loading the visible rows [0,1,2,3,4] (cell height is 70px).
When scrolling down the TableView, row [5] is loaded and fetching data from the server, but the problem is that beyond that point - the TableView is repeating those 6 rows instead of requesting new data from the server for the new rows...
But it does request new data for row [5], which is not visible (and not loaded) when the app first runs.
Anyone have any idea why is this happening?
Thanks!
EDIT: Here is my cellForRowAtIndexPath method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomCell";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
cell = (CustomCell *)currentObject;
NSString *appsURL = [NSString stringWithFormat:#"http://myAPI.com?id=%#",[app_ids objectAtIndex:indexPath.row]];
NSLog(#"row -> %d | id -> %#",indexPath.row,[app_ids objectAtIndex:indexPath.row]);
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:appsURL]];
[cell updateAppInfoForCellWithRequest:request];
break;
}
}
}
// Configure the cell...
return cell;
}
If you are setting data only if cell==nil, then that is your issue. UITable builds a cache of table view cells, only creating a new one if cell is nil. Therefore, you must set your data each time, i.e. outside of the cell==nil block.
The example below shows that process. First, grab a cell from the pool, if there isn't a free cell, create a new one. Set the cell's values for the appropriate row.
- (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];
}
id someData = [id_array objectAtIndex:indexPath.row]
cell.textLabel.text = [someData someString];
return cell;
}
dont know whats wrong with my table view, I have a dictionary(this dictionary is created by a sqlite operation) and I am trying to create cells like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
MyIdentifier = #"tblCellView";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if(cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = aCustomCell;
aCustomCell=nil;
}
NSMutableDictionary *tempDictionary=[[NSMutableDictionary alloc] init];
tempDictionary=[offendersNamesList objectForKey:[NSString stringWithFormat:#"key%d", indexPath.row+1]];
[[cell offendersImageView] setImage:[UIImage imageNamed:#"contact.png"]];
[cell.offendersNameLbl setText:[tempDictionary objectForKey:#"name"]];
[cell.offendersViolation setText:[tempDictionary objectForKey:#"offence"]];
[tempDictionary release];
//[cell setLabelText:[arryData objectAtIndex:indexPath.row]];
return cell;
}
all the items are displayed correctly but when I scroll the table view up the application crashes can you help me in this?
You are first allocating a new NSMutableDictionary and store it in tempDictionary. However, in the very next line, you overwrite that variable with a pointer to a new object (tempDictionary=[offendersNamesList objectForKey:[NSString stringWithFormat:#"key%d", indexPath.row+1]];). So you've got a memory leak here, the [[NSMutableDictionary alloc] init] is unnecessary, remove it.
Now, due to the naming conventions, the object that you've got from the offendersNamesList is autoreleased, yet you later call [tempDictionary release]; and thus over-release it. Remove that as well.
I have a UITableViewCell defined in a XIB (with outlets setup) and the custom class set to 'CustomTableViewCell'. I'm now looking to subclass the 'CustomTableViewCell' with a 'SubclassCustomTableViewCell', however I'd like to use the same XIB (and not need to redefine my view. Is this possible? I currently have:
+ (SubclassCustomTableViewCell *)create
{
// Create undefined cell.
id cell = nil;
// Iterate over NIB and find a matching class.
NSBundle *bundle = [NSBundle mainBundle];
NSArray *cells = [bundle loadNibNamed:#"CustomTableViewCell" owner:nil options:nil];
for (cell in cells) if ([cell isKindOfClass:[self class]]) break;
// Return cell.
return cell;
}
But I can't figure out to to get it to return anything but the parent.
+ (SubclassCustomTableViewCell *)create
{
// Create undefined cell.
id cell = nil;
// Iterate over NIB and find a matching class.
NSBundle *bundle = [NSBundle mainBundle];
NSArray *cells = [bundle loadNibNamed:#"CustomTableViewCell" owner:nil options:nil];
for (cell in cells) if ([cell isKindOfClass:[self class]]) return cell; //change this
// Return cell.
return nil; //change this
}
could you tell me where + (SubclassCustomTableViewCell *)create is defined, in which class
I know in vast of the cases I don't have to release static variable. However the following is the code for my model:
+ (UIImage*)imageForTag
{
static UIImage *imgTag;
if(imgTag == nil)
{
NSString* imageName = [[NSBundle mainBundle]
pathForResource:#"tag" ofType:#"png"];
imgTag = [[[UIImage alloc]
initWithContentsOfFile:imageName] autorelease];
}
return imgTag;
}
and here is my data table part
- (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];
}
if (indexPath.row == 0)
{
cell.imageView.image = [DataModel imageForSmtng];
}
else if(indexPath.row == 1)
{
cell.imageView.image = [DataModel imageForTag];
}
return cell;
This will crash on cell.imageView.image = [DataModel imageForTag] second time due to imageForTag is pointing to invalid address. If I add retain on that it will not crash. Is it wrong to remove autorelease from above and forget about imgTag references?
It is wrong. Because when you call autorelease on the imgTag variable, you just released the object it points to. But, the imgTag variable still points to that range of memory. So, when you call the imgTag again, it is not nil, it still points to something, an invalid thing.
So, the solution should be either:
1/ You shouldn't release it at all
2/ You have to release it manually when you think that it is a good time to release it. And then remember to do: imgTag = nil