UITableView random crash with cell.contentview set - objective-c

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]];

Related

Unrecognized selector sent to instance when creating cell

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;
}

Custom UITableViewCell Doesn't Draw Corectly

I'm having a really weird issue with my custom UITableViewCell. I have a cell with an identifier of "ThreadCell" in Interface Builder with some custom labels. These labels are tagged so I can access them.
In my code, I am doing the following:
static NSString *CellIdentifier = #"ThreadCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: #"ThreadCell"];
}
Person *person = [self.people objectAtIndex: indexPath.row];
UILabel *nameLabel = (UILabel *)[cell viewWithTag: 0];
nameLabel.text = person.nickname;
return cell;
This seems to work fine, with one exception. The TableView draws like this:
This obviously isn't my custom cell. But the weird thing is, when I tap on a cell, I get this:
This is my custom cell, drawn behind the default cell. What?! I'm not sure how this is happening because I do not ever set the title of the textview anywhere, so I'm not sure where the first John Smith comes from.
Anyone have any ideas?
In your code, you allocate a plain UITableViewCell and not an instance of your custom cell. Setting a reuseIdentifier in initWithStyle is not sufficient to load an instance of a custom cell class.
If you develop for iOS 5 and later, then you can use registerNib:forCellReuseIdentifier: to register a NIB file containing your custom cell. dequeueReusableCellWithIdentifier: will then always return an instance of that NIB.

Confusing syntax with custom UITableViewCell

I have found this code for a custom UITableViewCell :
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray* views = [[NSBundle mainBundle] loadNibNamed:#"MyCustomCell" owner:nil options:nil];
MyCustomCell *customCell = [[MyCustomCell alloc]init];
MyCustomCell.cellImage = [arrayImages objectAtIndex:indexPath.row];
for (UIView *view in views)
{
if([view isKindOfClass:[UITableViewCell class]])
{
cell = (MyCustomCell *)view;
}
}
}
and I could not figure how this specific part works: cell = (MyCustomCell *)view;
I wanted to change it for my previously created instance of MyCustomCell (customCell)... How could I do that?
Sometimes people will create a custom UITableViewCell using Interface Builder. This person is just loading their custom UITableViewCell subclass and assigning it to the cell. The line: cell = (MyCustomCell *)view; presumable works because MyCustomCell is a subclass of UITableViewCell.
This is just another technique for creating custom cells sometimes you'll see a similar thing done with tags.
First, it's iterating over a collection of UIViews, or UIView subclasses. It's storing each iteration's variable into a pointer called view.
It's then simply casting the current view variable to a type of MyCustomCell. Presumably MyCustomCell extends UIView, so this is legal.
This is useful if you want to use methods specific to MyCustomCell, as Xcode won't know they exist, if you don't explicitly Type Cast your object.

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?

Modal view controller is slow to appear

I'm having a hard time debugging an issue when presenting a modal view controller. I'm seeing a pause of between 0.5 seconds and 1 second between viewWillAppear being called and viewDidAppear being called on the presented (table view) controller. I tried replacing this with a bare bones table view controller to see if the issue was in the controller calling presentModalController, and it appeared quickly as expected.
I've peppered both controllers with NSLog statements in an attempt to diagnose the issue, but can't narrow it down further than a delay between viewWillAppear and viewDidAppear.
Short of rewriting the controller line by line, what's the best way for me to find out where the issue is? Are there any usual suspects here I should be aware of?
Edit : update with problematic code
The table view is displaying 2 cells, each containing a text field.
I have UITextField properties for each of the 2 text fields
#property (nonatomic, retain) IBOutlet UITextField *itemTextField;
and assign text fields to these properties as follows :
- (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) {
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(110, 10, 185, 30)];
textField.delegate = self;
cell.textLabel.text = #"Item";
textField.placeholder = #"Enter item name";
textField.keyboardType = UIKeyboardTypeDefault;
textField.returnKeyType = UIReturnKeyNext;
self.itemTextField = textField;
[cell addSubview:textField];
[textField release];
}
}
return cell;
}
I've left out the second row, but the code is the same.
If I comment out
self.itemTextField = textField;
the view loads as expected, but uncommented causes the slight delay I've been seeing. Should I be initialising this somewhere else rather than in cellForRowAtIndexPath? I'm a bit stumped.
Use Time Profiler in Instruments to see which is the offending code. Also note that excessive logging itself can cause noticeable speed degradation. Likely cases are costly methods to provide data to your table view, custom heights perhaps? Or loading of content from a network synchronously.
Got the same problem. Just fixed it with barrym's comment. Just move the becaomeFirstResponder code to viewDidAppear