Initialize custom UITableViewCell - cocoa-touch

I'm trying to load a single custom cell into a UITableView and it keeps throwing an error
UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:
I have no idea why. I have linked my table view cell to the UITableViewCell definition in my code, but it keeps giving me this error. Here is my code; any help would be greatly appreciated.
#import "RegisterDeviceViewController.h"
#implementation RegisterDeviceViewController
#synthesize checkString;
#synthesize cellRegistration;
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
}
return self;
}
*/
//Change UITableView Style to Grouped
- (id)initWithStyle:(UITableViewStyle)style {
// Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
style = UITableViewStyleGrouped;
if (self = [super initWithStyle:style]) {
}
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
self.title = #"Registration";
[super viewDidLoad];
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 1) {
if (indexPath.row == 1) {
return cellRegistration;
}
}
return nil;
}
//Pass search type over to rootViewController section2
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end

Okay. That's not how UITableView works. When the table view needs to draw a cell (ie, a row); it invokes tableView:cellForRowAtIndexPath: on the object specified in the dataSource property. It's your job to return a UITableViewCell from that method. This is how Apple does it (and how you should do it):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"AnIdentifierString"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"AnIdentifierString"] autorelease];
}
cell.textLabel.text = #"This text will appear in the cell";
return cell;
}
The number of times that method will be invoked depends on the number of sections in the table view and the number of rows in each section. The process works like this:
Table View invokes the delegate method numberOfSectionsInTableView: on its dataSource (it knows it implements that method because the dataSource must adhere to the UITableViewDataSource protocol).
If numberOfSectionsInTableView: returns a number greater than zero, the table view will invoke the delegate method tableView:numberOfRowsInSection: on the dataSource. So if numberOfSectionsInTableView: returns 2, tableView:numberOfRowsInSection: will be invoked twice.
If each invocation of tableView:numberOfRowsInSection: returns a number greater than zero, the table view will invoke the delegate method tableView:cellForRowAtIndexPath: on the dataSource' So if tableView:numberOfRowsInSection: returns 5, tableView:numberOfRowsInSection: will be invoked five times (once for each individual row).
Your opportunity to customise how that cell appears is after you've received a useable cell, but before it is returned (where 'This text will appear in the cell' appears above). You can do quite a lot here; you should see the Class Reference for UITableViewCell to see everything you can do (all I've done is set it to show 'This text...'). The lines above that are a way for iOS to reuse cells for performance considerations. If you, for example, wanted to show a certain string from an array of strings, you could do this (notice the use of the indexPath variable): cell.textLabel.text = [someArrayYouHave objectAtIndex:indexPath.row];.

You wrote:
it keeps throwing an error
'UITableView dataSource must return a
cell from
tableView:cellForRowAtIndexPath:' But
I have no idea why..
But your -tableView:cellForRowAtIndexPath: says, in part:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//...
return nil;
}
After reading the error message and looking at the code, do you not see the problem?

You are returning only one section, only one row
the section count and row count starts from 0.
Thats y you are getting this kinda error
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
if (indexPath.row == 0) {
//this checking is no necessary, anyway if you want use like this
//ensure that cellRegistration is UITableViewCell
return cellRegistration;
}
}
return nil;
}
Also refer this post for loading custom cells.

New iOS7+ solution optimized for Smoother Scrolling
You already can see old solutions but as far as huge amount of Apps will continue only iOS7+ support here is a way more optimized and correct solution.
Cell initialization
To initialize cell just call dequeueReusableCellWithIdentifier and iOS7+ systems are enough smart to handle if cell == nil or not. If during dequeue cell is nil system will automatically make a cell for you.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier" forIndexPath:indexPath];
return cell;
}
Cell configuration
Then do your entire cell configuration in willDisplayCell method. Just create one method in your class that configures cell and here you go with better performance!
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[self configureCell:cell forRowAtIndexPath:indexPath];
}
- (void)configureCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
// Configure your cell
}

Related

Collection View didSelectItemAtIndexPath segue

I'm struggling for a long time of this problem.
The reason was I had Collection delegate & datasource under the "CollectionView" not "CollectionView Controller" because I embedded one collectionView in the one of TableView Cell
but when I want to set up the
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)
{
}
This is my code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * Cell;
if (indexPath.section==0)
{
switch (indexPath.row)
{
case 0:
Cell = #"MoviesIntro";
break;
case 1:
Cell = #"影片介紹";
break;
case 2:
Cell = #"Moviesrelative";
default:
break;
}
}
 
UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:Cell];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:Cell];
}
switch (indexPath.row)
{
case 0:
{
}
break;
case 1:
{
}
break;
case 2:
{
Tony=(CollectionView*)[cell viewWithTag:741];
Tony.pageImages =imagearray;
[Tony reloadData];
}
break;
default:
break;
}
return cell;
}
Here is CollectionView.m Code
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
self.pic_url=[comment[indexPath.row] objectForKey:#"pic_url"];
self.name_zh=[comment[indexPath.row] objectForKey:#"name_zh"];
self.name_en=[comment[indexPath.row]objectForKey:#"name_en"];
self.intro=[comment[indexPath.row] objectForKey:#"short_Intro"];
self._id=[comment[indexPath.row] objectForKey:#"_id"];
self.trailer=[comment[indexPath.row] objectForKey:#"trailer"];
self.movieTime=[comment[indexPath.row] objectForKey:#"movieTime"];
self.picH_url=[comment[indexPath.row] objectForKey:#"picH_url"];
self.category=[comment[indexPath.row] objectForKey:#"category"];
self.director=[comment[indexPath.row] objectForKey:#"director"];
self.actor=[comment[indexPath.row] objectForKey:#"actor"];
self.language=[comment[indexPath.row] objectForKey:#"language"];
self.MyList=false;
[UIView animateWithDuration:0.05 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
} completion:^(BOOL finished) {
}];
}
Tony is Collection view.h/m files. pageImages is NSMutableArray on the Tony.h files.
to go to other view controller, property not found on object so I don't even know what can i do is anyone have good idea??
You can set add a delegate variable of your UITableViewCell and a delegate method like:
- (void) pushToViewController;
Then in YourTableViewCell.m file collectionView delegate methods add:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *){
[delegate pushToViewController];
}
And "tableView Controller"should be the delegate of your tableViewCell, in the controller of UITableView :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
YourTableViewCell *cell = ...
cell.delegate = self;
}
- (void) pushToViewController {
[self.navigationController pushViewController:someViewController animated:YES];
}
William Hu's suggestion will work.
In general you should not let your UI items implement their own delegates and data sources. It works but breaks the MVC pattern. Instead of that I'd use some custom UICollectionView to hold some reference to the row of the table to which it belongs. You could even 'abuse' the tag property for that (which I personally don't like but is is quite a common pattern) without the need to subclass UICollectionView.
Your UITableViewController can implement the datasource and delegate for the collection views too. On each call of any delegate method a reference to the very collectionView is handed into the method so that you can get the row of the table from there (which you have to set in the table view's cellForRowAtIndexPath of course) and go from there.
Therefore I'd rather suggest re-factoring your code and stick to the MVC pattern.

Static Table View Cell Labels are getting Squished into Header

EDIT
My constraints on custom cells are all weird. Please disregard. Just using standard Left Detail ones now.
I don't understand why in iOS8 now my static table view cell contents are collapsing into the header of each section.
There's really almost nothing in my SettingsTVC.m. This is a just a test with all static data in the storyboard:
- (void)viewDidLoad {
[super viewDidLoad];
}
Have you implemented TableView delegate methods properly i.e
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if(section == CUSTOM_SECTION)
{
return CUSTOM_VALUE;
}
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return height;
}

Populating NSTableView from NSMutableArray at button pressed

in an OSX app i'm currently developping to get familiar with obj-c, I want to populate a TableView. After some hours spent reading way too much blog posts, I can't understand how to add a row in my TableView.
Here is what I've done following this guide:
I have an NSMutableArray in my ViewController, this ViewController implement both interfaces NSTableViewDataSource and NSTableViewDelegate. And I implement both methodes as indicated in the guide. I also have a button and a tableView. When I click on the button, I fill my array with my own object, that's works great.
But what I want now, is when my array is populated, my tableview is too. I'm aware I need to bind those two in some way, but I have no idea how, can someone give some indication ?
Here is my code for my ViewController:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableViewEpisodes.delegate = self;
self.tableViewEpisodes.dataSource = self;
}
- (IBAction)btRefresh:(id)sender {
CalendarReader* reader = [[CalendarReader alloc]init];
self.episodes = [Episode getEpisodeFromEKEvents:[reader getLastMonthEventsForCalendarName:#"TV Shows"]];
[self.tableViewEpisodes reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(NSTableView *)tableView
{
return [self.episodes count];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColum row:(NSInteger)row {
// Retrieve to get the #"MyView" from the pool or,
// if no version is available in the pool, load the Interface Builder version
NSTableCellView *result = [tableView makeViewWithIdentifier:#"MyView" owner:self];
// Set the stringValue of the cell's text field to the nameArray value at row
result.textField.stringValue = [self.episodes objectAtIndex:row];
// Return the result
return result;
}
First, you are creating a cell view with an identifier which you have not declared, you need to do something like this (assuming you correctly adopted the UITableView protocol in your class):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *Ident = #"Ident";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Ident];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:Ident] autorelease];
}
[cell.textLabel setText: [yourArray objectAtIndex:indexPath.row];
return cell;
}
This is a delegate method for your NSTableView. It is called when the view is loaded so you need to provide a data source at runtime.
Second, I'm assuming you want one section with a number of rows equal to your data array. If this is so, you need to change the delegate method:
- (NSInteger)numberOfSectionsInTableView:(NSTableView *)tableView
to:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
Finally, keep in mind these above methods populate the ROWS, not the COLUMNS as you have it now. Once you populate your array, you need to invoke the method:
[yourTableView reloadData]
In order to refresh the table.
Hope this helps.
Thanks to #bryan-wheeler, I notice a message log when testing his code, and I found out, I was not implementing the correct method: here is my code for my ViewController now:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableViewEpisodes.delegate = self;
self.tableViewEpisodes.dataSource = self;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return 1;
}
-(id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex{
return [self.episodes objectAtIndex:rowIndex];
}
For beginner like me, you'll also have to implement the NSCopying protocol for the class stored in your data source array.
Right now, I only have one element in my TableView and it only show its memory address, but I'll update this answer as soon as I found out how to make it works for future beginner in my case.
EDIT: OK, it works !! My mistake was that: in the tableView:objectValueForTableColumn:row: method, I though I needed to return the Object representing the row given in parameter, but I had to return the one for the AND the cell given in parameter, now I found out, it's pretty obvious, but as a French, I didn't understand the method name correctly. Here is my code now:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableViewEpisodes.delegate = self;
self.tableViewEpisodes.dataSource = self;
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [self.episodes count];
}
-(id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex{
if([[aTableColumn title] isEqual: #"Serie's name"]){
return [[self.episodes objectAtIndex:rowIndex] seriesName];
}else if([[aTableColumn title] isEqual: #"Season number"]){
return [NSString stringWithFormat:#"%ld", (long)[[self.episodes objectAtIndex:rowIndex] seasonNumber]];
}else if([[aTableColumn title] isEqual: #"Episode number"]){
return [NSString stringWithFormat:#"%ld", (long)[[self.episodes objectAtIndex:rowIndex]episodeNumber]];
}else{
return nil;
}
}
There is some optimisation to do for sure, feel free to propose. But it's doing the job.

Retain Check Marks with Modal Segue

I am new to x-code and I was wondering: Is it possible to make check marks retain though you go through a modal segue?
I will check some boxes on my list:
But when I press done and then come back to the screen with a modal segue it appears as such:
Is it possible to make these checkmarks stay though I change views modally?
I have this code to create the checkmarks:
- (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.toDoItems count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ListPrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NewItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
cell.textLabel.text = toDoItem.itemName;
if (toDoItem.completed) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NewItem *tappedItem = [self.toDoItems objectAtIndex:indexPath.row];
tappedItem.completed = !tappedItem.completed;
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
When you pop or dismiss a view controller, that view controller is gone. However, you have a few options to remember the state that view controller was in. The easiest way is probably to store a global variable, maybe an NSArray, to remember the checked items. Then when you load this view controller, you could "check" any items that exist within that NSArray.
Note that this method will only work for the lifetime that the app is open. If they closed the app, it would be gone. If you wanted to maintain the "checked" items for the next time they opened the app, you would need to store it in the NSUserDefaults - data in there is available until the app is deleted from the phone.

Setting static cells in uitableview programmatically

I am programmatically creating a tableview in objective c. How can I make the cells static programmatically?
Thanks
Making cells static programmatically doesn't really make sense. Static cells are basically only for Interface Builder and requires the entire TableView to be static. They allow you to drag UILables, UITextFields, UIImageViews, etc. right into cells and have it show up just how it looks in Xcode when the app is run.
However, your cells can be "static" programmatically by not using an outside data source and hardcoding everything, which is usually going to be kind of messy and generally a poor idea.
I suggest making a new UITableViewController with a .xib and customizing it from there if you want "static" cells. Otherwise, just hardcode all your values and it's basically the same thing, but is probably poor design if it can be avoided.
By using a distinct cell identifier for each one you will get it. You could use something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = [NSString stringWithFormat:#"s%i-r%i", indexPath.section, indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
//you can customize your cell here because it will be used just for one row.
}
return cell;
}
You could also do it the old fashioned and just create the cell the way you want depending on the NSIndexPath, this works with Static Cell TVC's and regular table views (don't forget to return the proper number of sections and rows in their datasource methods):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
switch indexPath.row {
case 0:
// First cell, setup the way you want
case 1:
// Second cell, setup the way you want
}
// return the customized cell
return cell;
}
I you want to create cells structure for example for a settings screen or something like that and you maybe need just to modify some cells content but not their number or sections structure you can overload method of your UITableViewController subclass like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *aCell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
// Configure the cell...
if ([aCell.reuseIdentifier isEqualToString:#"someIdentifier"]){
//some configuration block
}
else if ([aCell.reuseIdentifier isEqualToString:#"someOtherIdentifier"]) {
//other configuration block
}
return aCell;
}
But you can make it in a better way with a little bit more code;
1) In the begining of your .m file add typedef:
typedef void(^IDPCellConfigurationBlock)(UITableViewCell *aCell);
2) add cellConfigurations property to your TablviewControllerSubclass extention:
#interface IPDSettingsTableViewController ()
#property (nonatomic, strong) NSDictionary *cellConfigurations;
#property (nonatomic) id dataModel;
#end
3) Modify your static cells of TableviewController subclass in storyboard or xib
and add unique cellReuseIdentifier for each cell you want to modify programmatically
4) In your viewDidLoad method setup cellsConfiguration blocks:
- (void)viewDidLoad
{
[super viewDidLoad];
[self SetupCellsConfigurationBlocks];
}
- (void)SetupCellsConfigurationBlocks
{
//Store configurations code for each cell reuse identifier
NSMutableDictionary *cellsConfigurationBlocks = [NSMutableDictionary new];
//store cells configurations for a different cells identifiers
cellsConfigurationBlocks[#"someCellIdentifier"] = ^(UITableViewCell *aCell){
aCell.backgroundColor = [UIColor orangeColor];
};
cellsConfigurationBlocks[#"otherCellIdentifier"] = ^(UITableViewCell *aCell){
aCell.imageView.image = [UIImage imageNamed:#"some image name"];
};
//use waek reference to self to avoid memory leaks
__weak typeof (self) weakSelf = self;
cellsConfigurationBlocks[#"nextCellIdentifier"] = ^(UITableViewCell *aCell){
//You can even use your data model to configure cell
aCell.textLabel.textColor = [[weakSelf.dataModel someProperty] isEqual:#YES] ? [UIColor purpleColor] : [UIColor yellowColor];
aCell.textLabel.text = [weakSelf.dataModel someOtherProperty];
};
weakSelf.cellConfigurations = [cellsConfigurationBlocks copy];
}
5) overload tableView:cellForRowAtIndexPath method like this:
#pragma mark - Table view data source
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *aCell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
// configure cell
[self configureCell:aCell withConfigurationBlock:self.cellConfigurations[aCell.reuseIdentifier]];
return aCell;
}
- (void)configureCell:(UITableViewCell *)aCell withConfigurationBlock:(IDPCellConfigurationBlock)configureCellBlock
{
if (configureCellBlock){
configureCellBlock(aCell);
}
}
It is pretty common to want to build a simple table to use as a menu or form, but using the built in API with the datasource and delegate callbacks don't make it easy to write or maintain. You may need to dynamically add/remove/update some cells, so using Storyboards by itself won't work.
I put together MEDeclarativeTable to programmatically build small tables. It provides the datasource and delegate for UITableView. We end up with an API where we provide instances of sections and rows instead of implementing datasource and delegate methods.