I'm seeking how create several cells to go to different ViewControllers.
For my TableView, I'm using a subclass of UITableViewController.
And when I choose 2 in the following method, I just see 2 identical cells which are doing exactly the same thing. I'm not interested by this. I don't even know their IndexPath in order to change their title.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 2;
}
And When I try to put another UITableViewCell in my TableView, it doesn't appear on iOS simulator, even with the same option (same subclass) than my first UITableViewCell which I can see.
Thanks for your help.
Edit : Here is my new code to create 2 cells but doesn't work :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell2";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[customCell alloc] init];
}
static NSString *CellIdentifier1 = #"Cell1";
UITableViewCell *cell1 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
if (cell1 == nil) {
cell1 = [[customCell alloc] init];
}
// Configure the cell...
return cell;
}
You define your cells in tableView:cellForRowAtIndexPath:, so you should provide an implementation for that method.
tableView:numberOfRowsInSection: only returns the number of cells in the table.
If you need more help, please provide your implementation for tableView:cellForRowAtIndexPath:. This is how a typical implementation looks like:
- (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];
}
... customize your cell ...
}
EDIT:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell2";
static NSString *CellIdentifier1 = #"Cell1";
if(indexPath.row == 0 ) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[customCell alloc] init];
}
} else {
UITableViewCell *cell1 = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
if (cell1 == nil) {
cell1 = [[customCell alloc] init];
}
}
return cell;
}
This method gets called when a cell has been selected. You can decide what you wanna do according to the selected row
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0)
[self goToFirstViewController];
else
if(indexPath.row == 1)
[self goToSecondViewController];
}
Use the following:
- (NSInteger) tableView: (UITableView*) tableView numberOfRowsInSection: (NSInteger) section
This delegate method returns the number of rows you want in that particular section. So if you want more than 2 rows, or you want the number of rows to be dynamic, you can create a NSArray in the AppDelegate or in the init method of the viewController class, and return the number in the numberOfRowsInSection method like
return [delegate numberOfNames];
In my example above, I created an array in my AppDelegate and also a method to return the number of objects I have in that array so that I can create the number of rows for my table.
- (UITableViewCell*) tableView: (UITableView*) tableView cellForRowAtIndexPath: (NSIndexPath*) indexPath
This delegate method will show what you want to display in each cell. Therefore, following on from my array created in my AppDelegate, I first create the cell, then I will set the text I want to display on the cell with a method I created in my AppDelegate that will return a NSString while taking in a NSInteger so that I can loop through my array and display the text accordingly.
static NSString* MyIdentifier = #"Default";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if( cell == nil )
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
cell.textLabel.text = [delegate nameAtIndex:indexPath.row];
}
nameAtIndex is the name of the method I created in my AppDelegate that will return the NSString object at the specific index (ie. the row number) from the NSArray I created to store all the items of my table.
When the user clicks on any of the rows in the table created, this delegate method will be called
- (void) tableView: (UITableView*) tableView didSelectRowAtIndexPath: (NSIndexPath*) indexPath
And in here, I will check if the text displayed matches any of the items in my array from the AppDelegate that stores the items in the table, and create the view that is necessary.
UIViewController* viewController = nil ;
NSString* nameInArray = [delegate nameAtIndex:indexPath.row] ;
if( [nameInArray isEqualToString:#"firstName"] )
{
viewController = [[FirstViewController alloc] init];
}
else if( [nameInArray isEqualToString:#"secondName"] )
{
viewController = [[SecondViewController alloc] init];
}
else if( [nameInArray isEqualToString:#"thirdName"] )
{
viewController = [[ThirdViewController alloc] init];
}
So with these 3 delegate methods, you will be able to create the table using a NSArray created, and be able to redirect the user to a viewController according to which option in the table he chooses. You will not have to keep editing the delegate methods if you choose to add more rows to the table as well since you are returning the count of the array when setting up the table.
The array and methods to get the data of the array can be created in the viewController as well, not necessarily in the AppDelegate, in case you were wondering.
The methods are as follows:
-(NSInteger) numberOfNames
{
return [myArray count];
}
-(NSString*) nameAtIndex: (NSInteger) index
{
return [myArray objectAtIndex:index] ;
}
Hope this helps! :)
Related
I'm new in CoreData and using MagicalRecord to rule with it. My problem is that I have the UITableView with an NSArray as dataSource populated with objects which fetched from CoreData db, and everything seems fine until I scroll the table for some times.
Here is my code:
Method for fetching data (MyDatabase.m):
+(NSArray *)getEntities{
...
return [MyEntity MR_findAllSortedBy:#"name" ascending:YES withPredicate:predicate];
}
Here is how I fetch and set data to UITableView in my ViewController:
- (void)viewDidLoad {
[super viewDidLoad];
myEntitiesArray = [MyDatabase getEntities];
if(myEntitiesArray.count != 0)
[myTableView setTableData:myEntitiesArray];
}
Here is setTableData method implementation in MyTableView.m:
- (void)setTableData:(NSArray *)array {
if (array && [array count] > 0) {
_tableData = array;
[self reloadData];
}
}
And here is how I set up my cells in MyTableView.m:
- (void)tableView:(UITableView *)tableView willDisplayCell:(SSCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.nameLabel.text = [(MyEntity *)_tableData[indexPath.row] name];
}
I tried to put an NSLog(#"name is %#",[(MyEntity *)_tableData[indexPath.row] name]) into willDisplayCell and found that when cells become empty, NSLog prints out the messages "name is (null)". I know this question is possibly solved by many people and many times before I faced this problem. Hope someone will help me to solve it too :)
UPDATE: cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"ssCell";
SSCell *cell = (SSCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if( !cell ) {
[self registerNib:[UINib nibWithNibName:#"SSCell" bundle:nil] forCellReuseIdentifier:cellIdentifier];
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
[cell setSelectedBackgroundView:selectedBackgroundView];
}
cell.nameLabel.text = [(MyEntity *)_tableData[indexPath.row] name];
return cell;
}
I also call this method inside MyTableView.m init method:
[self registerNib:[UINib nibWithNibName:#"SSCell" bundle:nil] forCellReuseIdentifier:#"ssCell"];
You have to use cellForRowAtIndexPath. In this method the cells are allocated.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
/*
* This is an important bit, it asks the table view if it has any available cells
* already created which it is not using (if they are offscreen), so that it can
* reuse them (saving the time of alloc/init/load from xib a new cell ).
* The identifier is there to differentiate between different types of cells
* (you can display different types of cells in the same table view)
*/
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
/*
* If the cell is nil it means no cell was available for reuse and that we should
* create a new one.
*/
if (cell == nil) {
/*
* Actually create a new cell (with an identifier so that it can be dequeued).
*/
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
/*
* Now that we have a cell we can configure it to display the data corresponding to
* this row/section
*/
cell.nameLabel.text = [(MyEntity *)_tableData[indexPath.row] name];
return cell;
}
You should be initializing the cell by calling init. Instead you are doing the following:
if( !cell ) {
[self registerNib:[UINib nibWithNibName:#"SSCell" bundle:nil] forCellReuseIdentifier:cellIdentifier];
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
[cell setSelectedBackgroundView:selectedBackgroundView];
The second call attempts to again reuse an existing cell when there isn't one available. That would probably return nil again.
be very careful of the "feature" of objective C, where calling a method of a nil object does nothing. Instead of crashing with null.pointer.exception like Java, it probably floats over [cell setSelectedBackgroundView:selectedBackgroundView] and a whole bunch of other lines without a problem.
I wish to display a custom view in between a table view if a particular cell's attribute matches some condition.
Something like this
Row 1
Row 2
Row (n) (matches condition)
--------------------------- (Custom View)
Row (n+1)
Row (n+2)
I am using a TableView and TableViewDelegate in a ViewController
You also need TableDataSource with the delegate. The just return another cell class, depending on the condition of the object in source array in the tableView cellForRowAtIndexPath: method of the table delegate.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
MyObjectClass * objectFromArray = [sourceArray.objectAtIndex: indexPath.row];
if(condition on MyObjectClass or index row){
static NSString *MyIdentifier = #"MyReuseIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
return cell;
}
}else{
static NSString *MyOtherIdentifier = #"MyOtherReuseIdentifier";
NewUITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[NewUITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyOtherIdentifier];
}
return cell;
}
}
}
I have a TableView with data from parse.com where I Placed order to descend according to Functional queryfortable with the "Date" selected by a pickerdate. Now the earliest date turns out to be the last cell and only in the last cell I would like to add a image ... In this specific case, how could I do? This is more 'complicated for me: (
I need to insert an image only in the last cell
Thanks Rory
If you're using PFQueryTableViewController then you should be using a custom subclass of it.
/// MyPFQueryTableViewController.h
#interface MyPFQueryTableViewController : PFQueryTableViewController
self.objects is the Datasource array the PFQueryTableViewController class uses for each table row.
You have to check whether the indexPath.row is the last object inside the self.objects array.
Within MyPFQueryTableViewController.m you override the cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"Cell";
PFTableViewCell *cell = (PFTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell
// Is this the last displaying cell?
if( indexPath.row == ([self.objects count]-1) )
{
cell.imageView.image = [UIImage imageNamed:#"End_Of_List.jpg"];
cell.textLabel.text = #"";
}
else
{
cell.textLabel.text = [object objectForKey:self.textKey];
}
return cell;
}
I have a UITableView with one section. All of the cells in that one section have cells that are derived from a subclass of UITableViewCell called PLContactCell.
What I'd like to do is, for the very last row of the table only, not use a PLContactCell. I just want to use a normal UITableViewCell that I can format however I would like. I'd also like to be able to have this cell NOT respond to being tapped.
My initial cellForRowAtIndexPath method is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PLContactCell *cell = [tableView dequeueReusableCellWithIdentifier:[PLContactCell reuseIdentifier]];
if (!cell) {
cell = [PLContactCell reusableCell];
cell.delegate = self;
}
id modelObject = [[sections objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
if ([modelObject isKindOfClass:[NSString class]]) {
[cell configureWithString:modelObject];
} else {
[cell configureWithUser:modelObject];
}
return cell;
}
EDIT
So I tried created a UITableView cell in the XIB file and added the reuse identifier of "newCell" to it. Then I added this code:
if (indexPath.row == [[sections objectAtIndex:indexPath.section] count] - 1) {
NSString *CellIdentifier = #"newCell";
noFormatCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
This doesn't do anything. My question is, how do I access the last row of the section and how do I make it so that that cell it is not a PLContactCell but a UITableView Cell.
If it's always at the end, you might consider using the footer view of the UITableView. You can then keep some extra logic out of your UITableViewDataSource.
If it HAS to be as a cell, you'd have to add an extra row count on your last section, then do an if statement check to watch out for it in your -tableView:cellForRowAtIndexPath: implementation. I would strongly urge you try the footer approach, as it's cleaner and way easier to figure out what you were doing a few months/years from now.
Here's some code. Note you'd need to make another section if you are using grouped style in the UITableView.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == sections.count - 1) //Looking for last section
{
return [sections objectAtIndex:section].count + 1; //Add one to the last section
}
return [sections objectAtIndex:section].count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row = indexPath.row;
if ((sections.count == indexPath.section) && [sections objectAtIndex:indexPath.section].count == indexPath.row)
{
//Here you would create and return your UITableViewCell
}
PLContactCell *cell = [tableView dequeueReusableCellWithIdentifier:[PLContactCell reuseIdentifier]];
if (!cell) {
cell = [PLContactCell reusableCell];
cell.delegate = self;
}
id modelObject = [[sections objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
if ([modelObject isKindOfClass:[NSString class]]) {
[cell configureWithString:modelObject];
} else {
[cell configureWithUser:modelObject];
}
return cell;
}
Apologies if this comes across as a beginner's question. I'm trying to populate a UITableView with sections and custom cell formatting.
I've created a customCell in ViewControl.xib which sits along the main view and looks like this:
customCell image
I have a dictionary to load up the values using a method in another class, depending on which row it's at. If it's in Row 1, load details for item 1 etc.
This is the code I'm using:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"customCell"];
// assigns current row's labels
NSArray * customCellText = [Model cellText:indexPath.row];
dinnerItem.text = customCellText[0];
dinnerDescription.text = customCellText[1];
dinnerTime.text = customCellText[2];
cell = customCell;
return cell;
}
And this is currently what's being generated:
iPhone simulator screenshot
The issues I have:
It's not populating all rows, only the last one.
I can only seem to click on the row which is populated, and even then
it stays selected as opposed to 'clicking on it'.
If I drag it up or down quickly it crashes.
I presume it has to do with the way it's redrawing/populating cells?
Thanks in advance!
SineTwo
EDIT, ADDED MORE CODE FOR CLARIFICATION:
ViewController.m
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [Model countKeys];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [Model rowsInSection:section];
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
// slightly crap code, this is initiated in viewDidLoad and is an array created by a method in Model.m. Only looks for keys and returns an array.
return headerKeys[section];
}
Model.m
+(NSArray *)headerKeys
{
NSArray *headerKeys = [[NSArray alloc] init];
headerKeys = [timerDictionary allKeys];
NSLog(#"All keys: %#", headerKeys);
return headerKeys;
}
+(NSArray *)customCellText
{
NSArray *customCellText = [[NSArray alloc]initWithObjects: #"dinnerItemText", #"dinnerDescriptionText", #"01:00", nil];
return customCellText;
}
+(NSInteger)rowsInSection:(NSInteger)sectionNumber
{
NSArray *keyContent = [[NSArray alloc] init];
keyContent = [timerDictionary objectForKey:dictionaryKeys[sectionNumber]];
NSLog(#"current section[%i]: %i", sectionNumber, [keyContent count]);
return [keyContent count];
}
+(NSArray *)cellText:(NSInteger)rowNumber
{
// display all dictionary keys, dictionaryKeys[x] will give back the specific category
dictionaryKeys = [timerDictionary allKeys];
// displays contents of first key in dictionary
NSArray *keyContent = [[NSArray alloc] init];
keyContent = [timerDictionary objectForKey:dictionaryKeys[0]];
// creates an array with all items within the selected key
NSArray *keyItems = [[NSArray alloc] initWithArray:keyContent[rowNumber]];
return keyItems;
}
I can say the following:
if you only do that:
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"customCell"];
prior to iOS 5 this doesn't guarantee you getting back a cell. Prior to iOS5 you have to do the following:
NSString *cellIdentifier = [NSString stringWithFormat:#"I%d-%d", indexPath.section, indexPath.row];
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"customCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
// assigns current row's labels
NSArray * customCellText = [Model cellText:indexPath.row];
dinnerItem.text = customCellText[0];
dinnerDescription.text = customCellText[1];
dinnerTime.text = customCellText[2];
cell = customCell;
return cell;
}
If you are on iOS5/6 these lines are not needed anymore:
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
But you have to use the following methods in the tableView setup:
registerClass:forCellReuseIdentifier: (available in iOS6 and later)
registerNib:forCellReuseIdentifier: (available in iOS5 and later)
Just hoping to solve your problem:
Don't forget the lines [...](cell == nil)[...]
First, change your method to this! You need to check if the cell is nil:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"customCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// assigns current row's labels
NSArray * customCellText = [Model cellText:indexPath.row];
dinnerItem.text = customCellText[0];
dinnerDescription.text = customCellText[1];
dinnerTime.text = customCellText[2];
cell = customCell;
}
return cell;
}