UITableViewController delegate - ios

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

Related

How do I get this UITableView to display?

I can't figure out how to get this UITableView to display.
The code creates the table fully programmatically except for the nib.
The nib is a simple cell that works in other UITableViews in the same app.
When the table view is created and added as a subview to the view and reloadData is called then numberOfSectionsInTableView: and tableView:numberOfRowsInSection: are both called. They both return 1. (I checked in the debugger.)
However tableView:cellForRowAtIndexPath: is never called.
I add the tableView to it's parent view with a NSLayoutConstraint that forces it's height, but nothing is displayed in that spot.
The view hierarchy debugger shows nothing in that spot and that there isn't a view on top of the table.
The app runs without any error or crashes.
I would appreciate your help.
#interface MyViewCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UIButton *button;
#end
#implementation MyViewCell
#end
#interface MyDataTableEntry : NSObject
#property (nonatomic, strong) NSString* title;
#end
#implementation MyDataTableEntry
#end
#interface MyDataTable : NSObject <UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) UITableView *tableView;
#property (strong, nonatomic) NSArray *rows;
#property (strong, nonatomic) UINib* nib;
#end
#implementation MyDataTable
- (UINib*)getAndRegisterNib:(NSString*)reuseIdentifier {
UINib* nib = [UINib nibWithNibName:reuseIdentifier bundle:nil];
[self.tableView registerNib:nib forCellReuseIdentifier:reuseIdentifier];
return nib;
}
- (id)initWithRows:(NSArray*) rows andView:(UIView*)view {
if (self = [super init]) {
self.rows = rows;
self.tableView = [[UITableView alloc] initWithFrame:view.bounds style:UITableViewStylePlain];
self.nib = [self getAndRegisterNib:#"PrototypeCell"];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.backgroundColor = [UIColor grayColor];
}
return self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.rows.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"PrototypeCell" forIndexPath:indexPath];
MyDataTableEntry* entry = self.rows[indexPath.row];
[cell.button setTitle:entry.name forState:UIControlStateNormal];
cell.button.backgroundColor = [UIColor blueColor];
return cell;
}
#end
Here is a snippet that creates the table:
MyDataTableEntry* entry = [[MyDataTableEntry alloc] init];
entry.title = #"hello";
self.dataTable = [[MyDataTable alloc] initWithRows:#[entry] andView:self.view];
[self.view addSubview:self.dataTable.tableView];
[self.dataTable.tableView setNeedsLayout];
[self.dataTable.tableView layoutIfNeeded];
[self.dataTable.tableView reloadData];
Without confirming in a debugger:
The problem is here:
self.dataTable = [[MyDataTable alloc] initWithRows:#[entry] andView:self.view];
[self.view addSubview:self.dataTable];
self.dataTable is an NSObject. The second line should read something like this:
[self.view addSubview:self.dataTable.tableView];
I would also suggest that you engage in some refactoring. MyDataTable is not a view. It's a controller. So rename to MyDataTableController. Then the bug would have been more obvious.
I would also ask why you are doing this in code? you are already using nibs. So why not go to storyboards. Doing that and using UITableViewController as a base class should enable you to remove a lot of the code and potential bugs.

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.

Populate UITableView from NSMutableArray

Having an issue where the array values do not display in my tableview cells, but can be printed out correctly with NSLog. Thanks in advance for your help!
TableViewCell .h
#import <UIKit/UIKit.h>
#interface TableViewCell : UITableViewCell
#property (strong, nonatomic) IBOutlet UIImageView *image;
#property (strong, nonatomic) IBOutlet UILabel *label;
#end
TableViewController.h
#import <UIKit/UIKit.h>
#interface TableViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate>
#property(nonatomic, strong) NSMutableArray *imagesArray;
#property(nonatomic, strong) NSMutableArray *namesArray;
#end
TableViewController.m
#import "TableViewController.h"
#import "TableViewCell.h"
#interface TableViewController ()
#end
#implementation TableViewController
#synthesize primaryWeaponNames = _primaryWeaponNames;
- (void)viewDidLoad {
[super viewDidLoad];
[self setupArrays];
}
- (void)setupArrays {
_namesArray = [[NSMutableArray alloc] initWithObjects:
#"NAME1", #"NAME2", #"NAME3"
nil];
self.imagesArray = [[NSMutableArray alloc] initWithObjects:
#"IMG1", #"IMG2", #"IMG3"
nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return _namesArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TableViewCell";
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.nameLabel.text = [NSString stringWithFormat:#"%#", [_namesArray objectAtIndex:indexPath.row]];
NSLog(#"%#", _namesArray[indexPath.row]);
cell.image.image = [self.imagesArray objectAtIndex:indexPath.row];
[cell setAccessoryType: UITableViewCellAccessoryDisclosureIndicator];
return cell;
}
When you make the cell in a xib file, you should register the nib in viewDidLoad. Since you didn't do that, the dequeue method will return nil, and you're just creating a default cell (in the if (cell == nil) clause) instead of your custom cell. The default cell doesn't have a nameLabel or image property, so the lines to set the values of those outlets won't do anything.
[self.tableView registerNib:[UINib nibWithNibName:#"Your nib name here" bundle:nil] forCellReuseIdentifier:#"TableViewCell];
In your TableViewController class, you need to include
self.tableView.delegate = self;
self.tableView.datasource = self;
in your viewDidLoad method. Or you can do this in interface builder by right click dragging the table view in your tableview controller to file's owner and setting delegate, then repeat for datasource

UIPopoverController showing empty frame

So I am trying to display a UITableView inside a UIPopoverController using the piece of code shown below
vc = [[ActionsViewController alloc] init];
initWithContentViewController:vc];
actionsController = [[UIPopoverController alloc] initWithContentViewController:vc];
actionsController.delegate = self;
NSLog(#"Try to sho it ");
[actionsController presentPopoverFromBarButtonItem:(UIBarButtonItem*)sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
And this is ActionsViewController.m which is a subclass of UITableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray* list = [[NSMutableArray alloc] initWithCapacity:4];
self.actionList = list;
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(320.0, 400.0);
[self.actionList addObject:#"Print as book"];
[self.actionList addObject:#"Print page"];
[self.actionList addObject:#"Save Page"];
[self.actionList addObject:#"Share"];
}
- (int)numberOfSectionsInTableView:(UITableView *)tableView
{
NSLog(#"in number of section");
return 1;
}
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString* lab = [self.actionList objectAtIndex:indexPath.row];
NSLog(#"here in there");
cell.textLabel.text = lab;
return cell;
}
- (NSInteger)numberOfRowsInSection:(NSInteger)section
{
return [self.actionList count];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.delegate != nil) {
[self.delegate actionSelected:indexPath.row];
}
}
The corresponding .h file
#import <UIKit/UIKit.h>
#protocol KLActionsViewControllerDelegate
- (void)actionSelected:(NSInteger)index;
#end
#interface KLActionsViewController : UITableViewController <UITableViewDataSource,UITableViewDelegate>
#property (nonatomic, retain) NSMutableArray* actionList;
#property (nonatomic, assign) id<KLActionsViewControllerDelegate> delegate;
#end
Also, I don't think the functions cellForRowAtIndexPath and numberOfSectionsInTableView are getting called because I don't see any console output.
EDIT: This is what I see in the popover
:
In viewDidLoad I added self.tableView = [[UITableView alloc] init]
I guess, the problem would be in that line:
vc = [[ActionsViewController alloc] init];
if you have a NIB file, you should load the UIViewController with using it.
vc = [[ActionsViewController alloc] initWithNibName:#"ActionViewController" bundle:nil];
// the NibName must be the exact name of XIB file without extension.
OR/AND
you should set the popoverContentSize:
[actionController setPopoverContentSize:vc.view.frame.size];
I hope it helps a bit for you.
UPDATE:
it seems it is not an UIPopoverController problem, just a simple UITableViewController delegate issue.
in that line:
actionsController.delegate = self;
you should set that class as delegate which implements the UITableViewDelegate. basically it is the UITableViewController itself like
actionsController.delegate = actionController;
therefore you should not replace this in that case if you haven't implemented the delegate callback methods in the self which the new delegate class is. you simple steal the chance from the class which was really delegated to answer the callbacks, and in you new delegate class you don't answer the callbacks.
this would be the reason why you are seeing the empty table, without the datas.
You need to load the table view via (last line of viewdidload :
[self.tableView reloadData];
that will trigger the table view methods to action. Put a breakpoint in the various tableview methods particularly in -
(NSInteger)numberOfRowsInSection:(NSInteger)section
{
return [self.actionList count];
}
If this does not get called your tableview is not loaded.
UITableViewController is implicit set datasource and delegate, there is no need to set explicitly datasource and delegate.
Remove UITableViewDataSource,UITableViewDelegate from .h file
#import <UIKit/UIKit.h>
#protocol KLActionsViewControllerDelegate
- (void)actionSelected:(NSInteger)index;
#end
#interface KLActionsViewController : UITableViewController
#property (nonatomic, retain) NSMutableArray* actionList;
#property (nonatomic, assign) id<KLActionsViewControllerDelegate> delegate;
#end
and also remove this line **actionsController.delegate = self;
vc = [[ActionsViewController alloc] init];
actionsController = [[UIPopoverController alloc] initWithContentViewController:vc];
[actionsController presentPopoverFromBarButtonItem:(UIBarButtonItem*)sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

UITableView crash on scroll or tap - issue with ARC?

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)

Resources