Unrecognized selector sent to instance when creating cell - objective-c

I'm trying to reuse cell created in my storyboard,
I'm using this code:
MyTableView * mytableview;
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
mytableview = [sb instantiateViewControllerWithIdentifier:#"myTable"];
AlertCell *cell;
cell = [mytableview.tableView dequeueReusableCellWithIdentifier:#"alertCell"];
cell.message.text = #"some text";
return cell;
I get this error:
[__NSArrayM objectForKeyedSubscript:]: unrecognized selector sent to instance 0xe02b7d0
this line eventually generates the error:
[mytableview.tableView dequeueReusableCellWithIdentifier:#"alertCell"];
The table view when using the cell is programaticlly created.
Everything i wanted works! but i got the errors in the log anyway
Thanks a lot!

You cannot create the table view when the cell is created.
You have to create the table view in a method that i called when the view controller is created, such as viewDidLoad.
Then in the cell method use the tableView pointer that is included in the method.
Also, the error actually indicates a problem with your datasource as well.
There are so many apparent knowledge gaps, maybe the best way is to read through the Table View Programming Guide.

I didn't exactly understand when you are creating table. You should not instantiate at the same time. Why you needed to call this line?
mytableview = [sb instantiateViewControllerWithIdentifier:#"myTable"];
If you want to use tableview, just create it normally and add it to your viewcontroller instead of using above line of code in viewDidLoad.
UITableView *tableView = [[UITableView alloc]initWithFrame:tableFrame style:UITableViewStylePlain];
tableView.rowHeight = 45;
[self.view addSubview:tableView];
tableView.delegate = self;
tableView.datasource = self;
Implement tableview's data source and delegate methods. And in cellForRowAtIndexPath::
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"newFriendCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"newFriendCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//do your stuff
return cell;
}

Related

UITableView random crash with cell.contentview set

i am working on a uitableview and i am getting random crashes with when i use custom uiviewcontroller inside a uitableviewcell.
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(!cell){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
SuspectSalesResponse* r = [valueArray objectAtIndex:indexPath.row];
SuspectSalesCellViewController* scellView = [[SuspectSalesCellViewController alloc]initWithNibName:#"SuspectSalesCellViewController" bundle:nil cellData:r];
if(![cellViewArray containsObject:scellView])
[cellViewArray addObject:scellView];
[cell.contentView addSubview:scellView.view];
}
return cell;
This code sometimes work, sometimes it gives the folowing error:
* -[SuspectSalesHeaderViewController _parentModalViewController]: message sent to deallocated instance 0x8556880
i've tried keeping contentviews within an array (cellViewArray) but no luck i am still getting this error. What am i doing wrong?
Thanks for helping!
The problem is that you are not supposed to add view controllers inside table cells. You should try to change from UIViewController to UIView.
The exact reason for your crash is that you create a controller (which has a view) and you are adding the view to your cell, but the controller object is lost. When the view sends whatever notification to it's parent controller, the controller is already deallocated and there is where you get your bad access. This will be solved if you transform your SuspectSales~ to a UIView subclass
EDIT
To load a UIView (or any subclass) from a nib file, you must first create the xib, set the view's class to your class (you do it in the third tab on the interface builder).
After that, you can load it with this code:
Generic function in some utility class
+ (id)loadNibNamed:(NSString *)nibName ofClass:(Class)objClass {
if (nibName && objClass) {
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:nibName owner:nil options:nil];
for (id currentObject in objects ){
if ([currentObject isKindOfClass:objClass])
return currentObject;
}
}
return nil;
}
In your class:
Subclass *view = [UtilityClass loadNibNamed:#"Subclass" ofClass:[Subclass class]];

pushing tableview results in cellForRowAtIndexPath exception

I am trying to build a multilayer xmlparser which shows data in a tableview and switches to the next view based on the selected row (most likely to the same viewcontroller).
I started with storyboard segues but as i am using dynamic cells i dont know how to create more than one push segue (because it needs to push to various viewcontrollers). So i kept the storyboard views, deleted all the segues and used the code below instead.
however it throws this exception when the new view tries to pupulate the row:
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'UITableView dataSource
must return a cell from tableView:cellForRowAtIndexPath:'
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"index: %i", indexPath.row);
switch (indexPath.row) {
case 0:
{
DetailViewController *detailViewController = [DetailViewController alloc];
[self.navigationController pushViewController:detailViewController animated:YES];
break;
}
//...
default:
{
LayerViewController *layerViewController= [LayerViewController alloc];
[layerViewController setStartUpWithIndex:indexPath.row andLayer:layercount];
[self.navigationController pushViewController:layerViewController animated:YES];
break;
}
}
}
setStartUpWithIndex:andLayer: is my init method...
the problem does not occur when im pushing via storyboard segue with the following segue code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
[[segue destinationViewController] setStartUpWithIndex:indexPath.row andLayer:layercount];
}
i think i am missing something the "segue" method does which i need to include in my didSelectRowAtIndexPath as cellForRowAtIndexPath: works fine in the first layer of the view.
requested edit:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"LayerCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.textLabel.text = [[articles objectAtIndex:indexPath.row]objectForKey:#"HerstellerName"];
// cell.textLabel.text = #"ololol";
return cell;
}
the articles array is working btw
You aren't creating any cells. I don't know why Apple didn't include this in the default implementation, but after:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
You need to add:
if (cell==nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
Otherwise you're just relying on dequeued cells which won't exist when the tableView loads.
Edit: And if you're not using ARC, you need to wrap that alloc method with an autorelease.
You are pushing another viewController to be viewed and this next viewController has no dataSource methods implemented. This what happened.
As far as I understood, you are trying to present an xml node with a table in the way that each table row consist child nodes of presented node. selecting row with chld node gets you to next table view which presents table with child nodes of selected node.
IMO you may need only one view controller to present current xml node, which changes only data to be presented in a table and reloads the table again. So no pushing view controllers is needed. You can use then method
– reloadRowsAtIndexPaths:withRowAnimation:
for all the nodes in the table, setting animation similar to changing view controller (aka sliding old table left)

How can I check if a reuse identifier has been registered with a UITableView already?

In iOS apps, we have to register nib files with our table view before we can use UITableView#dequeueReusableCellWithIdentifier.
Example:
static NSString *myReuseIdentifier = #"MyReuseIdentifier";
UINib *cellNib = [UINib nibWithNibName:myReuseIdentifier bundle:nil];
[self.tableView registerNib:cellNib forCellReuseIdentifier:myReuseIdentifier];
Is there a way to check if a Nib has already been registered with a UITableView?
I have a custom cell that I use in various tables across several controllers in my app. I'd like to move some of the code to a macro. Something like
-(CustomCell *)customCell:(UITableView *)tableView
{
static NSString *reuseIdentifier = #"MyReuseIdentifier";
if (![table hasAlreadyRegisteredNib:reuseIdentifier]){
UINib *cellNib = [UINib nibWithNibName:reuseIdentifier bundle:nil];
[self.tableView registerNib:cellNib forCellReuseIdentifier:reuseIdentifier];
}
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
return cell;
}
I am not sure if it that what you intend, but
-dequeueReusableCellWithIdentifier:
returns nil if the cell is not ready to reuse. Otherwise, it returns the cell, so you can simply try.
I believe the point of registerNib:forCellReuseIdentifier: is to reduce boilerplate code. Could you simply call this once in your viewDidLoad method?

Properties in custom UITableViewCell are not getting initialized for a UISearchDisplayController's table

I am using a UISearchDisplayController to be able to display a table with custom cells based on some data I am retrieving from a server.
First I set the UISearchDisplayController inside my UIViewController.
self.searchController = [[UISearchDisplayController alloc]
initWithSearchBar:self.mySearchBar contentsController:self];
self.searchController.delegate = self;
self.searchController.searchResultsDataSource = self;
self.searchController.searchResultsDelegate = self;
My UIViewController also implements the UISearchBarDelegate, so I can determine when a search starts. I set up a block so when my api call returns it gets called and a dictionary of results is saved in the self.searchResults property:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
// here we make the api call
[api getSomeInfo:searchBar.text complete:^(NSDictionary *json) {
self.searchResults = json;
[self.searchController.searchResultsTableView reloadData];
}];
}
Now, the problem I have is that in my UITableViewDataSource method, where I return the custom cell. My cell is instantiated, but it's IBOutlets never get initialized, so I cannot set their content (text, images, etc) properly:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchController.searchResultsTableView) {
cell = [tableView dequeueReusableCellWithIdentifier:#"SearchResultsCellIndentifier"];
if (cell == nil) {
cell = [[SearchResultsCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.customLabel.text = [self.searchResults objectForKey:#"customText"]; // cell exists but cell.customLabel is nil!!
}
}
Why is the content nil? Is there somewhere in my Custom Cell class where I should be setting the content up?
Thanks!
I think your problem is that you used the variable cellIdentifier when creating the cell, but a string constant when dequeuing.
Simply always recreating a cell will work, but is not efficient at all and leads to major memory leaks.
You should first set the cellIdentifier according to which table view you are in, and which kind of cell you need, then dequeue with that cellIdentifier, and then create a new one if needed.

Populating a TableView with object data on button click within a UIViewController

I am trying to populate a TableView situated within a UIViewController with a collection of objects when a button is clicked.
The problem is that cellForRowAtIndexPath seems to be expecting 'votes' to be an instantiated object, which it isn't until the button is pressed.
I'm not sure I'm going about this the correct way and would appreciate any assistance anybody could give me.
I have specified the delegate and datasource as follows:
#interface MyViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>
I have completed my implementation of numberOfRowsInSection as follows:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.votes count];
}
I have completed my implementation of cellForRowAtIndexPath as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Votes";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the region cell
CandidatePhrase *phrase = [self.votes objectAtIndex:indexPath.row];
cell.textLabel.text = phrase.phrase;
return cell;
}
On button press I'm loading an array with a list of objects
_votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil];
I'm just now clear on how the table will bind each time I press the button.
Here's the error I'm currently getting, presumably because the votes array hasn't been instantiated?
2011-12-09 22:34:48.979 MyApp[3809:fb03] -[NSObject tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x6b49b20
The tableView instance (not the view controller) is going to call its delegate methods as soon as it is instantiated and whenever it is informed of a change to the table view. So it doesn't matter if votes is instantiated or not for the delegate method to be called.
However, if you want it to know that there are no rows when votes has not been instantiated, try this
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (votes != nil)
return [self.votes count];
else
return 0;
}
Once you have instantiated votes, you want to call reloadData on your tableView.
Don't forget to add your view controller as the delegate for the tableView, if it is not already.