I have a UITableView, named tblParentComments in a UIView, of class CBox.
I have definitely set my view as the datasource and delegate of my table view, and my view does implement those protocols. The method tableView:numberOfRowsInSection: does get called and returns a non-zero value. But the function tableView:cellForRowAtIndexPath: is never called.
I noticed that if I put the method tableView:cellForRowAtIndexPath: in comments, Xcode does NOT stop compiling with an error like "tableView:cellForRowAtIndexPath: is required" -- the app just runs and show a empty table.
I don't understand. Any ideas? This is the code for my view:
Interface CBox.h
#interface CBox : UIView <UITableViewDelegate, UITableViewDataSource>
And in the implementation file:
- (id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
tblParentComments = [[UITableView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.frame.size.width, frame.size.height)];
tblParentComments.delegate = self;
tblParentComments.dataSource = self;
//tblParentComments.userInteractionEnabled = NO;
tblParentComments.backgroundColor = [UIColor clearColor];
tblParentComments.separatorStyle = UITableViewCellSeparatorStyleNone;
tblParentComments.bounces = NO;
[self addSubview:tblParentComments];
}
return self;
}
#pragma mark - UITableViewDelegate + UITableViewDatasource
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"num of rows = %d", parentComents.count);
return 1; // I set a non-zero value for test
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
.... // I set a breakpoint here, never been called here
}
YES..i have the same problem... and I just found out the solution.
In my class i use different inits with different parameters.
In my -(void)viewDidLoad i use to alloc the table view with CGRectZero, and ONLY in this case IF u DONT set up the FRAME of the UITableView then:
the numberOfRowsInSection will BE CALLED
the cellForRowAtIndexPath will NEVER BE CALL
I just set up my UITableView frame and it's works.
As I read above comments I can figure out couple of things:
You probably have messed up a bit structure of your code. You should always conform to protocols in your view controller - ! not view. Alternatively, what I like to do (as it gives me better control over my code and it keeps things clean), separate protocols out of view controller - means create new object (model object) that will handle everything what table requires and it will conforms to table delegate and datasource.
If you organise your code wisely, you should avoid situation you described.
Also I believe you may have 2 objects conforming to table protocols, and thats where the things get ugly.
Related
Help me to get rid of with this dilemma that occurred yet when I tried to dequeued the cell (Custom Cell).Below are some steps and Indents that I did with my Project.
The very first is I drag and drop a UITableView in my ViewController and add the ViewController.h doing after this
#interface ViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
Then I made a Custom Cell with 3 UILabels and change the height of the Cell to 65.
After that I made a property in ViewController.m
#property (nonatomic,strong) NSMutableArray *myTodoTitles;
Then in method(ViewDidLoad) I did.
myTodoTitles = [NSMutableArray arrayWithCapacity:10];
[myTodoTitles addObject:#"Go for ride"];
[myTodoTitles addObject:#"Do University Assignments"];
[myTodoTitles addObject:#"Watch Show"];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.myTodoTitles count]-1 inSection:1];
[self tableView:self.myTodoTable cellForRowAtIndexPath:indexPath];
After that I just did these things in my ViewController.m
#pragma mark - Table view data source
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *myIdentifier = #"TodoCell";
TodoCell *todoCell = (TodoCell*)[tableView dequeueReusableCellWithIdentifier:myIdentifier];
todoCell.todoTitleLabel.text = [self.myTodoTitles objectAtIndex:indexPath.row];
return todoCell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [myTodoTitles count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
But when I run the project it dequeued nothing.
Please help
Most likely that you have not connected your viewController to be the dataSource of your tableView. This could be done from Interface Builder or from the code. You can easily check it by adding self.myTodoTable.dataSource = self; at the very first of viewDidLoad method.
And also: what did you mean by `
[self tableView:self.myTodoTable cellForRowAtIndexPath:indexPath];`
in viewDidLoad ? Seems like you wanted to do
[self.myTodoTable reloadData];
There are to UITableView methods with similar name:
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
and
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
forIndexPath:(NSIndexPath *)indexPath
The first one will try to dequeue a reusable cell. If it returns nil you are responsible to create appropriate cell.
The latter one will always return a valid cell: you will have to register a certain class or NIB with that tableview beforehand though. Docs.
EDIT:
As ReDetection pointed out: first method will also return a valid cell as long as you had registered a proper class (or nib) with that tableview.
In your case that means that you should register TodoCell in viewDidLoad with your tableView.
If TodoCell is made without .xib:
[self.tableView registerClass:[ToDoCell class]
forCellReuseIdentifier:#"TodoCell"];
Or if it is made with .xib.
[self.tableView registerNib:[UINib nibWithNibName:#"TodoCell"
bundle:nil]
forCellReuseIdentifier:#"TodoCell"];
EDIT2:
Your code also seems to be missing the dataSource setting. Something like:
self.tableView.dataSource = self;
This will trigger initial reload.
You'd probably want to set a delegate (since your controller claims to adopt that protocol) in the same manner.
I am fairly new to this Native App dev - I have built an app which contains a UITableViewController to display messages - all works fine - but for styling reasons I need to change it from a TableViewController to a tableview embedded within a viewcontroller.
I have made a view controller containing a table view and relevant linked custom cells / fields and altered the associated header file to -
#interface NotificationsListTVController : UIViewController
but my table methods no longer fire and I'm not sure how to instantiate them?
(code below)
#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 self.GPTNotifications.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
static NSString *CellIdentifierRead = #"CellRead";
UITableViewCell *cell;
notifications *n = [self.GPTNotifications objectAtIndex:indexPath.row];
if (n.read == false) {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
CustomCellRead *cellReadB = (CustomCellRead *)cell;
cellReadB.notifTitle.text = n.notifTitleD;
cellReadB.notifDate.text = n.notifDateD;
cellReadB.notifMsg.text = n.notifMessage;
return cellReadB;
}
else {
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierRead forIndexPath:indexPath];
CustomCell *cellReadB = (CustomCell *)cell;
cellReadB.notifTitle.text = n.notifTitleD;
cellReadB.notifDate.text = n.notifDateD;
cellReadB.notifMsg.text = n.notifMessage;
return cellReadB;
}
}
Are you setting the delegate and datasource of your tableview to your class?
Something like:
self.myTableView.delegate = self;
self.myTableView.dataSource = self;
When you create a UITableViewController this is done for you, but if you add the table yourself you need to set them.
Also:
#interface NotificationsListTVController : UIViewController <UITableViewDelegate, UITableViewDataSource>
I do it this way in Interface Builder:
Make your TableViewController
Make your ViewController and add a ContainerView to it
Delete the segued embedded ViewController that comes with it
Select the ContainerView and draw a connection from viewDidLoad to your TableViewController
you'll get only once option: embed
Done. Your TableViewController will now get displayed within your ViewController.
Pass whatever Data you need forward from the ViewController to the TableViewController with the embedded Segue.
Make the following changes in NotificationsListTVController.h:
#interface NotificationsListTVController : UIViewController<UITableViewDataSource,UITableViewDelegate>
Also in NotificationsListTVController.m, dont forget to provide these two statements as well.
tableView.delegate=self ;
tableView.dataSource=self;
These are required to set the delegate methods.These two statements need to be provided after initializing your tableView. Like for instance :
tblView = [[UITableView alloc] initWithFrame:CGRectMake(100,200,320,420) style: UITableViewStyleGrouped];
tblView.delegate = self;
tblView.dataSource = self;
[self.view addSubview:tblView];
These methods you are referring to are the delegate methods which cannot be fired directly unlike other ordinary methods.
Hope it Helps!!!
I got a big app containing a lot of dependencies. For this case I implemented a class called RootTableViewController to handle all the stuff that has to be done everytime a table view controller is required.
Now I discovered an endless loop and I dont know how to fix it. I got the following code in RootTableViewController:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
NSString *sectionTitle = [self tableView:tableView titleForHeaderInSection:section];
int numbersOfRowInSection = [self.tableView numberOfRowsInSection:section];
if (numbersOfRowInSection > 0)
{
// ...
}
else
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 28.0f)];
return view;
}
}
This works perfect on iOS 5 and iOS 6, but on iOS4 it causes an endless loop, because [tableView numberOfRowsInSection] is calling [tableView viewForHeaderInSection]. How can I fix this using the table view api? Its no solution for me to work with the [ count] of internal data arrays because I got a lot of table view controllers extending this RootTableViewController with different data sources.
This is simply not good style. You are supposed to subclass or rahter implement the related delegate method but you shoudl not call UITableView.numberofRowsInSection:
However, you have certainly implemented tableView:numberOfRowsInSection. Move all of its functionality to the new method myNumberOfRowsInSection: In there do the same. It is mainly a copy of your current numberOfRowsInSection.
Then here in your code sniplet call [self myNumberOfRowsInSection:...];
And within tableView:numberOfRowsInSection:section just do:
return [self myNumberOfRowsInSection:section];
Apply the same pattern to all delegate methods that you may want to call yourself. Move all its business logic into your own method and then only call your own method from the delegate method and from your own code.
If you want to get the number of rows in a section of the data source without accessing the internal data array, you could query the dataSource delegate for it, something like
int numbersOfRowInSection = [self.tableView.dataSource tableView:self.tableView numberOfRowsInSection:section];
(not compiler checked)
I have a subclassed UITableView, 'subclassChild' which inherits from another subclass 'subclassParent'. 'subclassParent' then inherits from UITableView.
My view controller inherits from a custom View Controller 'GenericTableViewController', and contains my UITableViewController which has the class of 'subclassChild'.
'GenericTableViewController' customises my tables header and footers for all view controllers containing a table view.
My question is, how do I make it so that my UITableView can use 'subclassChild' and 'subclassParent' as its delegate and dataSource. Still making sure that 'GenericTableViewController' customises my tables header and footers.
Sorry if this is a bit confusing, I've tried to describe it as well as I can.
P.S I have added <UITabBarDelegate, UITableViewDataSource> in the header files.
And I have made sure that the table inherits from 'subclassChild' in IB.
Thanks in advance!
What you can do, is have your 'GenericTableViewController' have a delegate itself. It will do most of the work itself, but at the end it shall ask its delegate 'anything else to do'?
And when the delegate says 'Yes, there is something actually', that code will get executed, serving as data source, delegate or whatever you want it to be. As an example (please forgive me for code inaccuracies):
- (void) rowForIndex:(NSInteger)row {
if([delegate respondsToSelector:#selector("TVGetRow:")]) {
return [delegate TVGetRow:row]
} else return Rows[row];
}
This example assumes, that you have an array of tableview cells, but should give you a good idea. You don't need to implement UITableViewDataSource to that subclass header files, but instead use a custom protocol.
Edit: Since apparently I was unclear in what I was aiming to do, allow me to add a little information.
The basic idea is to use 'subclassChild' and through it 'subclassParent' as delegates for GenericTableViewController, instead of using 'subclassChild' as the type for the tableView. (You should never subclass a view, unless you intend to change it's drawing behaviour).
The general hierarchy would work like this:
subclassChild -> delegate -> GenericTableViewController <- DataSource/Delegate <- TableView
You'd need to create an init method, or a setter method, which sets this delegate, and a simple protocol, which allows you to call functions, like so:
#interface GenericTableViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate> {
id theDelegate;
}
- (id) initWithDelegate:(id)delegate;
#end
#protocol GTVC
#optional
- (UITableViewCell *) getDataForRow:(NSIndexPath *)path;
- (void) rowSelected:(NSIndexPath *) path;
#end
In the .m file, this example would look like this:
#implementation GenericTableViewController
- (id) initWithDelegate:(id)delegate {
self = [super initWithNibName:#"TableView.nib" bundle:nil];
if(self) {
theDelegate = [delegate retain]; //The 'retain' is optional, really.
}
return self;
}
- (void) dealloc {
[theDelegate release]; //In case of retain -> release.
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if([theDelegate respondsToSelector:#selector(getDataForRow:)])
return [theDelegate getDataForRow:indexPath];
else {
//Create general UITableViewCell
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if([theDelegate respondsToSelector:#selector(rowSelected:)])
[theDelegate rowSelected:indexPath];
else {
//Do stuff
}
}
#end
This protocol/delegate 'game' can be expanded easily, giving you all levels of control you need...all the while with the GenericTableViewController as a 'backup', if any delegate isn't set, or 'not ready' to answer the request for further instructions.
I hope this helps.
I am loading new views for a small iphone app, and was wondering how to pass details from one to another?
I am loading a tableview full of data from and xml file, then once clicked a new view is brought in via:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SubInfoViewController *subcontroller = [[SubInfoViewController alloc] initWithNibName:#"SubInfoView" bundle:nil];
[self presentModalViewController:subcontroller animated:YES];
[subcontroller release];
}
Next step would be to tell the newly loaded view which row had just been loaded?
Any idea, thoughts more than welcome, and please be gentle big newbie...
I typically create my own init method to do things like this. I think it would likely be better to pass in the corresponding "model" object represented by the tableView row, rather than the row number itself, like this:
In SubInfoViewController.h
#interface SubInfoViewController : UIViewController {
YourObject *yourObject;
}
#property (nonatomic, retain) YourObject *yourObject;
Then in SubInfoViewController.m:
- (SubInfoViewController*)initWithYourObject:(YourObject*)anObject {
if((self = [super initWithNibName#"SubInfoView" bundle:nil])) {
self.yourObject = anObject;
}
return self;
}
You'd create and present it this way:
// assuming you've got an array storing objects represented
// in the tableView called objectArray
SubInfoViewController *vc = [[SubInfoViewController alloc] initWithYourObject:[objectArray objectAtIndex:indexPath.row]];
[self presentModalViewController:vc animated:YES];
[vc release];
This could be adapted pretty easily to allow you to pass in any type of object or value (such as a row number if you still want to do that).
Add an instance variable to your view controller and declare a property corresponding to it, so after you alloc, init it, set it like subcontroller.foo = Blah Blah.