Unrecognized selector sent to instance Custom Cell - objective-c

I am trying to make a Custom Cell for my UITableView.
So, I create a class (UITableViewCell) and I named HomeCell. This class has a xib file with label named titleCell.
In my cellForRowAtIndexPath method I wrote:
HomeCell *cell = (HomeCell *)[tableView dequeueReusableCellWithIdentifier:#"HomeCell"];
if(cell == nil){
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:#"HomeCell" owner:self options:nil];
for(id oneObject in xib){
if([oneObject isKindOfClass:[HomeCell class]]){
cell = (HomeCell *) oneObject;
}
}
}
//Get object at index data
HomeObjectCell *tempObject = [self.dataForCell objectAtIndex:indexPath.row];
NSLog(#"Title : %#",tempObject.title); // The tempObject.title return a NSString example
cell.titleCell.text = tempObject.title; // <-- ERROR !
When I run the application, I got:
[UITableViewCell titleCell]: unrecognized selector sent to instance 0x6e61890
Any idea?

Are you absolutely sure that:
1) .. your class "HomeCell" is named correct for the table cell? Select the cell, select 'identity inspector' -> section 'Custom Class' and take a look
2) .. referencing outlets with connections to each label/ui image is made? Try select your cell again, select 'Connections inspector' and take a look

In the description you mentioned that HomeCell has member named titleLable.
In the code you have used cell.titleCell.text.
So, I think you should use cell.titleLabel.text instead of cell.titleCell. The crash message is also suggesting same thing that there is no proprty with cell.titleCell exists in HomeCell.

Check to see if the file owner in the xib file is of Class HomeCell.

Related

Objective-C / Load cell from xib / Need explanation

Ok, I know how to load a custom cell from a xib, via this code:
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustpmCellView" owner:self options:nil];
cell = (CustomCell *)[nib objectAtIndex:0];
But can someone explain what does the first row do?
I feel really stupid typing that every time and not knowing how exactly it works.
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustpmCellView" owner:self options:nil];
loadNibNamed returns all views under your xib. So we are holding that in array. Say here all view under CustpmCellView will be fetched and saved in array nib.
cell = (CustomCell *)[nib objectAtIndex:0];
We are getting first view from array, as that is our desired view, and then we are casting and assigning to cell object.
We need to assign new view to each cell in UITableView, so for that purpose every time new cell is needed, and we do that using above code snippet.
EDIT
[NSBundle mainBundle] , is explained at What the meaning of [NSBundle mainBundle] in iPhone?
According to Docs
loadNibNamed:owner:options:
Unarchives the contents of a nib file located in the receiver's bundle.
- (NSArray *)loadNibNamed:(NSString *)name owner:(id)owner options:(NSDictionary *)options
Parameters
name
The name of the nib file, which need not include the .nib extension.
owner
The object to assign as the nib’s File's Owner object.
options
A dictionary containing the options to use when opening the nib file. For a list of available keys for this dictionary, see “Nib File Loading Options.”
Return Value
An array containing the top-level objects in the nib file. The array does not contain references to the File’s Owner or any proxy objects; it contains only those objects that were instantiated when the nib file was unarchived. You should retain either the returned array or the objects it contains manually to prevent the nib file objects from being released prematurely.

Trouble setting properties and performing actions on a tableviewcell outside of cellForRowAtIndexPath:

I have a custom tableviewcell that I create a pointer to in the header of my view controller and init inside cellForRowAtIndexPath
if ([[cellOrder objectAtIndex:indexPath.section] isEqualToString:#"balanceCell"]) {
balanceCell = (BalanceCell *) [tableView dequeueReusableCellWithIdentifier:#"balanceCell"];
if (balanceCell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"BalanceCell" owner:self options:nil];
balanceCell = (BalanceCell*) [nib objectAtIndex:0];
}
return balanceCell;
}
I only have one instance of balanceCell in the table view, so I assumed that if I wanted to set any properties of the cell, I could just refer to balanceCell
However, this is not working. When the user presses a button, the method below is called (I verified that it is actually being called). However, the methods called on balance cell don't work.
- (void)addBalanceCell {
[cellOrder addObject:#"balanceCell"];
[table reloadData];
balanceCell.leftEquation = equationCell.leftView.equationOrder;
balanceCell.rightEquation = equationCell.rightView.equationOrder;
[balanceCell setUpText]; // not called
}
What is the proper way to reference balanceCell?
Well this isn't the interface builder way but you should have an array that functions as your datasource.
That datasource should contain the objects that define a BalanceCell.
If you can't manage those objects and reload the table then you might want to consider changing it so.
A UITableViewCell should mainly be used as a View, a means to show the data, Model, that is stored somewhere else aka your datasource.
At the moment you do "addBalanceCell" you should make a new instance of the dataObject and manage your data here. After you manage it with the data you want you can just add if to your datasource and update the table, which means you then made a new cell since there is a new entry in the datasource.
The same way you update these cells by accessing those dataObjects in your datasource. You can ask for the object in your datasource and manage it the way you want it to. If you then update the table the cell should update itself with the new data.
In short, your cell should mainly hold the means to show data, not the data itself.

Can't get contents of file from a .plist file into an array

I'm trying to create a create a simple app where I have a TableView that should display the contents of a file. I have created a Table View in IB and dragged it's delegate and data source to the file's owner and I have manually created a .plist file with 1 array that have 2 items.
In my TableViewController.h i have declared an array.
NSArray * posts;
In my implementation file I have declared the required methods for UITableViewDataSource like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"Returning num sections");
return posts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// create a cell
UITableViewCell * post = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"post"];
// fill it with content
post.textLabel.text = [posts objectAtIndex:indexPath.row];
// return it
return post;
}
And in my ViewController 'viewDidLoad' method I try to add the content of my file to the 'posts' array like this:
- (void)viewDidLoad
{
NSString * postFile = [[NSBundle mainBundle] pathForResource:#"Posts" ofType:#"plist"];
posts = [[NSArray alloc] initWithContentsOfFile:postFile];
NSLog(#"%#", postFile);
NSLog(#"%i", posts.count);
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
NSLog(#"%i", posts.count); returns 0, despite that I have added values to my .plist file. And nothing is displayed in the table view.
Suggestions on how so solve this would be appreciated.
I think you need to reload your table after you've loaded your postFile NSArray. If your view controller is a UITableViewController, try adding the following line of code to the end of your viewDidLoad method:
[self.tableView reloadData]
(On an unrelated note, you should also make your call to the super class the first thing you do in the viewDidLoad method, hence the comment the xcode template gives you.)
Edit: Problem with count.
I think you also have a problem with your debugging. count isn't a property of NSArray, so you can't use the dot syntax with it. You should be sending a message to your NSArray instance i.e. [posts count].
Ok, it seems like Xcode 4 creates plists with Dictionary as it's root type. If you want to use an array you have to open the .plist file in another text editor (probably doable in Xcode too) and change < dict >< /dict /> to < array >.
Also, it wasn't necessary to use an array at all. This also worked:
// Changed my array to a dictionary.
NSDictionary * posts;
// Get the cell text.
NSString * cellText = [[NSString alloc] initWithFormat:#"%i", indexPath.row];
// fill it with content
post.textLabel.text = [posts valueForKey:cellText];

Multiple UiTableViewCell subclasses in a single UiTableView? Why is this code not working

Okay so Here's my issue. I have a table, a bit like Apple's addressBook.app that uses different cell subclasses to display information in different ways. On this particular app I have 3 different UiTableViewCell subclasses, all with their own nibs.
I can't get the code right :-( if I try and access the instance variable of my cell in the CellForRowPath method it says' (ERROR request for member 'type' in something not a structure or union') Now I assume that the word 'something' is the root of my problem.. The code I am about to paste obviously doesn't setup my cell properly. Here is the code:
There are 3 if statements to get the right cell subclass depending on the section being called:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *PoolFacilityCellIdentifier = #"PoolFacilityCellIdentifier";
static NSString *PoolFacilityAddressCellIdentifier = #"PoolFacilityAddressCellIdentifier";
static NSString *PoolFacilityPoolCellIdentifier = #"PoolFacilityPoolCellIdentifier";
NSUInteger section = indexPath.section;
//Creat a generic container for cell which will be cast during one of the next two statements
id cell;
//Change the identifier depending on cell type. MIGHT NEED TO ADD SOMETHING ELSE FOR POOLS AND PRICES
if (section == KAddressIndex) {
cell = (PoolFacilityAddressCell *)[tableView dequeueReusableCellWithIdentifier:PoolFacilityAddressCellIdentifier];
if (cell == nil) {
//Load the appropriate nib for the type of cell being asked for.
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PoolFacilityAddressCell" owner:self options:nil];
for (id object in nib) {
if ([object isMemberOfClass:[PoolFacilityAddressCell class]])
cell = (PoolFacilityAddressCell *) object;
}
}
}
else if (section == KPoolIndex) {
cell = (PoolFacilityPoolCell *)[tableView dequeueReusableCellWithIdentifier:PoolFacilityPoolCellIdentifier];
if (cell == nil) {
//Load the appropriate nib for the type of cell being asked for.
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PoolFacilityPoolCell" owner:self options:nil];
for (id object in nib) {
if ([object isMemberOfClass:[PoolFacilityPoolCell class]])
cell = (PoolFacilityPoolCell *) object;
}
}
}
else {
cell = (PoolFacilityCell *)[tableView dequeueReusableCellWithIdentifier:PoolFacilityCellIdentifier];
if (cell == nil) {
//Load the appropriate nib for the type of cell being asked for.
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"PoolFacilityCell" owner:self options:nil];
for (id object in nib) {
if ([object isMemberOfClass:[PoolFacilityCell class]])
cell = (PoolFacilityCell *) object;
}
}
}
Any help would be greatly appreciated!!! I import all the header files for my cell subclasses.
The code that throws my error is this (specifically the cell.type.text = thePool.type) cell.type is an Iboutlet UiLabel:
if (section == KPoolIndex) {
NSMutableArray *thePools = [allSections objectForKey:sectionAsNumber];
if ([thePools count] > 0) {
[cell setPromptMode:NO];
//Set the label and Pool from the Pool Object
Pool *thePool = [thePools objectAtIndex:row];
cell.type.text = thePool.type;
}
}
Thanks,
Dan
Matt Gallagher has an article that lays out a cleaner way to accomplish this type of table.
He breaks out the cell specific code into their own classes. Makes it alot easier to manage.
cocoawithlove.com - Heterogeneous cells in a UITableViewController
In your code, this line looks like trouble
cell.type.text = thePool.type;
thePool is a "Pool" object, does it have a property named "type" ?
Check that the #property and #synthesize are setup correctly for "type" in your "Pool" object.
I believe it may be due to the fact that your cell is declared as id and you're trying to access a property off one of your cell subclasses. You can send all the messages you want to an object of type id, but trying to access properties on an object of type id will fail at compile time. Also, be sure to import the .h files containing your subclasses.
I might have a few answers to my own question:
1 - As drew has mentioned the cell remains of type ID. I thought my following if functions would typeCast the cell into being a specific cell type but that doesn't seem to be the case.
2 - A solution would be to have 3 big if statements for each type of cell. Each if statement ending in a
return cell;
call.
3 - The solution I'm going to try now is to have only one cell subclass instead of 3 and have a method in my cell subclass called
-(void)setCellBehaviour:(int)definedNumber
That way I can hide layers/labels etc and customize it how I wish.
update
I've achieved the required results like this:
I only have one cell subclass which has a single NIB with many different UIlabels.
I call this in the cellForRowAtIndexPath: method of my uiviewcontroller, once the cell has been alloc/init.
//Set the behaviour
[cell setCellBehaviour:KCellBehaviourName];
This references the definitions I set in the header file of the view controller:
//Definitions for cell type behaviour. Passed to cell during creation.
(HASH)define KCellBehaviourStandard 0
(HASH)define KCellBehaviourName 1
(HASH)define KCellBehaviourAddress 2
(HASH)define KCellBehaviourPool 3
(Cant seem to do hashes in this post)
Then my UITableViewCellSubclass has this methos which we called earlier:
-(void)setCellBehaviour:(NSUInteger)definedBehaviour {
switch (definedBehaviour) {
case KCellBehaviourStandard:
self.label.hidden = NO;
self.value.hidden = NO;
self.length.hidden = YES;
self.poolType.hidden = YES;
break;
case KCellBehaviourName:
self.label.hidden = NO;
self.value.hidden = NO;
self.length.hidden = YES;
self.poolType.hidden = NO;
break;
case KCellBehaviourAddress:
self.label.hidden = NO;
self.value.hidden = NO;
self.length.hidden = YES;
self.poolType.hidden = YES;
break;
case KCellBehaviourPool:
self.label.hidden = NO;
self.value.hidden = NO;
self.length.hidden = NO;
self.poolType.hidden = YES;
default:
break;
}
}
Oh my goodness this is much simpler than all of the answers are making it appear. I'm not going to discuss your overall design, just focus on why your code is not working.
This is the line that is triggering the error:
cell.type.text = thePool.type;
You can fix it by replacing that with these lines:
PoolFacilityCell* poolCell = (PoolFacilityCell *)cell;
poolCell.type.text = thePool.type;
The dot-syntax for accessing properties only works if the compiler knows what class you are working with. It can't deal with cell.type because cell is declared as type id, so at compile time it doesn't know that type is supposed to be an Objective-C property. The error message calls it "something not a structure or union" because it thinks you are treating cell like a plain C struct, which it is not, hence the error.
Incidentally, the lines where you write:
cell = (PoolFacilityAddressCell *) object;
are meaningless. Since cell is declared as id (any generic object), there is no need for the cast. Based on an earlier comment, you seem to think that type casting like that affects the cell variable somehow. It does not. The line above compiles into instructions that simply copy the memory address of the instance from object to cell. Type casting is there to avoid warnings and errors at compile time.

UICachedDeviceWhiteColor and NSAutoRelease for table cell mem leaks

I have a memory leak that displays UICachedDeviceWhiteColor. I'm not using UICachedDeviceWhiteColor anywhere and a search on it turns up people saying this is a bug in the iPhone-SDK. I found this blog entry:
http://piezoelectrics.blogspot.com/2009/02/uicacheddevicewhitecolor-leak-in-iphone.html
but I can't find
#import "NSAutoreleasePool.h"
I get an "error: NSAutoReleasePool.h: no such file or directory". Is there a fix for this memory leak or a correct way to allocate table cells from nibs?
Here's how I'm currently doing it:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"CellNameIdentifier"];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CellName" owner:self options:nil];
//cellName is IBOutlet to XIB's tablecell. I reference it several times in this calss
cell = cellName;
}
return cell;
}
I don't see an alloc here so why would there be a mem leak? Could this be a problem:
#property (nonatomic, retain) IBOutlet UITableViewCell *cellName;
Because of your property declaration, the sythesized setter for your cellName property will retain the object passed to it.
You should send a release message to cellName in your dealloc method.
Furthermore, there is no need to load the nib every time the cellView is requested. Either check if cellName != nil and return it or set the reuseIdentifier on the cellView so that it can be found by dequeueReusableCellWithIdentifier.
Actually, if you're using a NIB for your table view cell (not normally necessary unless you're doing something really custom) you will have to load it each time you didn't get a hit on the reusable table view cell. I think the following code looks a bit cleaner:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyID"];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CellName"
owner:self options:nil];
cell = [nib objectAtIndex:1];
}
The objectAtIndex:1 trick works if the cell is the first object in the NIB (the zero object is the file owner).
Some notes for doing table view cells:
Don't retain your cell objects either implicitly by assigning to a property or manually. This will make the the reusable table cell functionality not work properly since it can't free the cell memory.
Don't forget to set the cell reuse identifier in interface builder since you can't do it in code if you're using a NIB.
Always make sure the cell is autoreleased. Either do it manually, or ensure you're using a cocoa function that returns autoreleased memory (as the objectAtIndex: method does).