UITableView crash on scroll or tap - issue with ARC? - uitableview

I am trying to create a simple UItableview app in a project utilizing ARC. The table renders just fine but if I try to scroll or tap a cell the app crashes.
Looking at the NSZombies (is that the proper way to say that?) I get the message "-[PlacesViewController respondsToSelector:]: message sent to deallocated instance 0x7c29240"
I believe this has something to do with ARC as I have successfully implemented UItableviews in the past but this is my first project using ARC. I know I must be missing something very simple.
PlacesTableViewController.h
#interface PlacesViewController : UIViewController
<UITableViewDelegate,UITableViewDataSource>
#property (nonatomic, strong) UITableView *myTableView;
#end
PlacesTableViewController.m
#import "PlacesTableViewController.h"
#implementation PlacesViewController
#synthesize myTableView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.myTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
[self.view addSubview:self.myTableView];
}
#pragma mark - UIViewTable DataSource methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 100;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *result = nil;
static NSString *CellIdentifier = #"MyTableViewCellId";
result = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(result == nil)
{
result = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
result.textLabel.text = [NSString stringWithFormat:#"Cell %ld",(long)indexPath.row];
return result;
}
#end

There is nothing obviously wrong with the code you posted. The problem is with the code that creates and holds onto PlacesViewController. You are probably creating it but not storing it anywhere permanent. Your PlacesViewController needs to be saved into an ivar or put in a view container that will manage it for you (UINavigationController, UITabController or similar)

Related

Difficulty trying to implement separate UITableViewDelegate

I have a UITableView in my app and I'm trying to pull it's delegate methods into a separate UITableViewDelegate. This is what the code looks like:
RestaurantViewDelegate *delegate = [[RestaurantViewDelegate alloc] initWithRestaurant:self.restaurant andRecommended:self.recommended];
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 235.0f, self.view.frame.size.width, self.view.frame.size.height-235)];
self.tableView.delegate = delegate;
self.tableView.dataSource = delegate;
[self.view addSubview:self.tableView];
And this is what RestaurantViewDelegate looks like:
// RestaurantViewDelegate.h
#interface RestaurantViewDelegate : NSObject <UITableViewDelegate>
#property (nonatomic, strong) NSArray *recommendations;
#property (nonatomic, strong) Restaurant *restaurant;
- (id)initWith Restaurant:(Restaurant *)restaurant andRecommended:(NSArray *)recommendations;
#end
and
// RestaurantViewDelegate.m
#implementation RestaurantViewDelegate
#synthesize recommendations = _recommendations;
#synthesize restaurant = _restaurant;
- (id)initWith Restaurant:(Restaurant *)restaurant andRecommended:(NSArray *)recommendations {
self = [super init];
if ( self != nil ) {
_recommendations = recommendations;
_restaurant = restaurant;
}
return self;
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Recommendations: %d", [_recommendations count]);
return [_recommendations count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 48.0f;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
return cell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
#end
However, when I run my app and click on a cell, all cells disappear. I really don't know what's causing this. Any ideas as to what I'm doing wrong?
This is a very interesting problem. Remember that in ARC (Automatic reference counting ), an object will be retained only as long as a strong reference to it is maintained. Remember, the 'delegate's are always weak, and in your case it means, once you come out of the scope, where you create the delegate object and setup the table view, there will no longer any delegate object retained. This is the reason you might not see anything happening when you try to reload the table view. Make the delegate object RestaurantViewDelegate a member of your controller. And check..

UITableView from separate class

Case is following: I want to create UITableView from separate class.
Currently I have following:
// Menu.h
#interface Menu : UITableViewController <UITableViewDelegate, UIAlertViewDelegate> {
UITableView *tableView;
}
#property (nonatomic,retain) NSMutableArray *navigationItems;
- (void)initMenu:(UIView *)view;
#end;
Then
// Menu.m
#import <Foundation/Foundation.h>
#import "Menu.h"
#implementation Menu
- (void) initMenu:(UIView *)view {
self.navigationItems = [[NSMutableArray alloc] initWithObjects:#"One",#"Two",#"Three",#"Four",#"Five",#"Six",#"Seven",#"Eight",#"Nine",#"Ten",nil];
UIView *mainmenu=[[UIView alloc]initWithFrame:CGRectMake(0, 50, 320, 420)];
[mainmenu setBackgroundColor:[UIColor yellowColor]];
[view addSubview:mainmenu];
UITableView *menutableView = [[UITableView alloc] initWithFrame:view.bounds style:UITableViewStylePlain];
menutableView.backgroundColor = [UIColor whiteColor];
menutableView.delegate = self;
menutableView.dataSource = self;
[mainmenu addSubview:menutableView];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableViewi cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableViewi dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [self.navigationItems objectAtIndex:indexPath.row];
return cell;
}
#end
And in different .m file I call method:
...
#import "Menu.h"
...
- (void)viewDidLoad
{
[super viewDidLoad];
...
Menu *menu = [[Menu alloc] init];
[menu initMenu: self.view];
}
Running this will crash application and Xcode won't give any detailed report. However, if I combine Menu.m to the .m file where I'm calling "initMenu" it won't crash.
Also if I comment out menutableView.dataSource = self; it will run with our crash (no rows in table of course...).
Passing a view into an init method and then creating/adding subviews in said init method is an odd design pattern. Change your Menu class to a viewcontroller, then move your UIView and UITableView declarations to the viewDidLoad method. The crash is likely happening because the tableview is trying to display data before its parent view has finished loading.

Trying to implement table view and getting Thread 1: signal SIGABRT error?

I have been trying to implement a simple table view in Xcode. However I have run into this error:
[UIView tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x7fd23bc76bc0
I searched for an answer online, and basically all I could find out was that I'm trying to send that method something that does not exist. If someone could please explain what exactly is going on here, why it is going, and how I might fix it, I would be grateful. Here is the code:
In ViewController.h :
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
}
#end
In ViewController.m :
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
{
NSArray *recipes;
}
- (void)viewDidLoad {
[super viewDidLoad];
recipes = [NSArray arrayWithObjects:#"Eggs", #"Bacon", #"Ham", #"Steak", #"Bread", #"Lettuce", #"Tomatoes", #"Carrots", #"Milk", #"Pizza", #"Chicken", #"Soup", #"Cheese", nil];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [recipes count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *simpleTableID = #"SimpleTableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableID];
}
cell.textLabel.text = [recipes objectAtIndex:indexPath.row];
return cell;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
If you need anymore information, I'd be happy to get it to you. Thanks.
It looks like you have dragged the delegate property of the table view onto a UIView instead of the UiViewController.
In the storyboard make sure you drag the delegate property to the controller (yellow circle at the top of the view).

How can I return a TableView from a instance method?

I am trying to add a TableView as a subview in my RootViewController.The TableView will come from another ViewController(TableViewGeneratorController) instance method.
So,What is the best way to do this?
I have created a TableViewGeneratorController it works fine as a standalone app. Then from my RootViewController I have created one instance of the TableViewGeneratorController and trying to call the instance method prepareField,which will return the TableView. I got the TableView but
numberOfRowsInSection and cellForRowAtIndexPath is not getting called.
TableViewGeneratorController.h
#import <UIKit/UIKit.h>
#interface TableViewGeneratorController:UIViewController<UITableViewDataSource,UITableViewDelegate>
{
UITableView *tableView;
}
#property(strong,nonatomic)UITableView *generatedTbleView;
- (UITableView *)prepareField;
#end
TableViewGeneratorController.m
#import "TableViewGeneratorController.h"
#import "RootViewController.h"
#interface TableViewGeneratorController (){
}
#end
#implementation TableViewGeneratorController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (UITableView *)prepareField
{
tableView = [[UITableView alloc]initWithFrame:CGRectMake(10, 80, 300, 500)];
tableView.dataSource = self;
tableView.delegate = self;
tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"Cell"];
[tableView reloadData];
return tableView;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIndentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndentifier];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIndentifier];
}
cell.textLabel.text = #"Yes";
return cell;
}
#end
RootViewController.m
Here I am trying to add the TableView as a subview.
TableViewGeneratorController *tableViewGeneratorController = [[TableViewGeneratorController alloc]initWithNibName:#"TableViewGeneratorController" bundle:nil];
UITableView *tv = [tableViewGeneratorController prepareField];
[self.view addSubview: tv];
What is the problem going on?
Thanks
What you are doing is not really the proper way.
The immediate issue is that the TableViewGeneratorController instance that you create goes out of scope and is deallocated. This leaves the table view with not existing data source or delegate. A simple workaround is to assign the TableViewGeneratorController instance to an instance variable instead of a local variable.
But the proper solution is to embed the TableViewGeneratorController as a child controller of the root view controller.
Change TableViewGeneratorController to be a UITableViewController and get rid of the prepareField method.
Then when you create the TableViewGeneratorController, you add it as a child controller. See the docs for UIViewController for details.

UITableViewController delegate

I have a UITableViewController class that I call "MasterViewController".
From within MasterViewController I want to display a tableview that uses a different UITableViewController (not MasterViewController). I am doing this as another tableview is already using MasterViewController as its delegate and datasource.
I have the following logic in a method of MasterViewController;
ToTableViewController *toController = [[ToTableViewController alloc] init];
UIView *toView = [[UIView alloc] initWithFrame:CGRectMake(10,10,250,200)];
//toView.backgroundColor = [UIColor redColor];
UITableView *toTableView = [[UITableView alloc] initWithFrame:CGRectMake(10,10,220,180) style:UITableViewStylePlain];
[toTableView setDelegate:toController];
[toTableView setDataSource:toController];
[toView addSubview:toTableView];
[self.view addSubview:toView];
[toTableView reloadData];
I want the ToTableViewController to be the delegate and datasource for this new tableview (toTableView).
The problem is that my ToTableViewController cellForRowAtIndexPath method is not being called. In fact, none of the delegate methods are being called.
Any feedback would be appreciated.
Tim
I am pretty sure your issue is that the toController is being released too early. Using ARC (I assume you are too), I played around with it following what you are trying todo. And I got the same result where the delegate methods appeared NOT to get called. What solved it was to NOT use a local variable for the toController. Instead declare it as a member to the MasterViewController class like
#property (strong, nonatomic) ToTableViewController *toController;
then use the variable name _toController to refer to it in the code.
EDIT: just to be clear in my first test I had ToTableViewController inherit from a UITableViewController. Since as such I really didn't need that added UITableView you are creating and attaching the delegates too (you could just use the _toController.view directly) SO on this second test I created a ToTableViewController from scratch inheriting from the delegate protocols where the separate UITableView becomes necessary. Just for completeness here is the code that works:
ToTableViewController.h
#import <Foundation/Foundation.h>
#interface ToTableViewController : NSObject <UITableViewDelegate, UITableViewDataSource>
#end
ToTableViewController.m
#import "ToTableViewController.h"
#implementation ToTableViewController
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 13;
}
- (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];
}
NSString *str1 = #"Row #";
cell.textLabel.text = [str1 stringByAppendingFormat:#"%d", indexPath.row+1];
return cell;
}
#end
MasterViewController.m (I declare the toController in the .m file as shown but could do so the .h file instead...)
#import "MasterViewController.h"
#import "ToTableViewController.h"
#interface MasterViewController ()
#property (strong, nonatomic) ToTableViewController *toController;
#end
#implementation MasterViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_toController = [[ToTableViewController alloc] init];
UIView *toView = [[UIView alloc] initWithFrame:CGRectMake(10,10,250,200)];
toView.backgroundColor = [UIColor redColor];
UITableView *toTableView = [[UITableView alloc] initWithFrame:CGRectMake(10,10,220,180) style:UITableViewStylePlain];
[toTableView setDelegate:_toController];
[toTableView setDataSource:_toController];
[toView addSubview:toTableView];
[self.view addSubview:toView];
}
#end

Resources