I have a very strange situation in my app and i can't explain or locate the bug.
I have a UIViewController with a tableView in it.
In the table view I have 3 prototype cells, also i have 2 section that are divided like so:
first section: row 0 (cell ID: episodeScrollersCell)
second section: row 0 (cell ID: addCommentsCell)
: row 1+ (cell ID: commentCell)
The required methods in the protocol are listed below.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger rowNum;
if(section == 0){
rowNum = 1;
}else{
rowNum = 1; // here is the problem. If i change the number of row to above 1
}
return rowNum;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier;
if(indexPath.section == 0){
cellIdentifier = episodeScrollersCell;
}else if (indexPath.section == 1 && indexPath.row == 0){
cellIdentifier = addCommentsCell;
}else{
cellIdentifier = commentCell;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
return cell;
}
Now the problem arises when I want to have 2 rows or above in the second section (i.e to have 2 prototype cells in one section), the view Controller won't show. I've logged the cellForRowAtIndexPath: method to see if the cells gets loaded and they do.
Any advice?
Thanks,
What you seem to be doing is trying to dequeue a reusable cell with a chance that no cell has been created before. However, if no cells have been created/allocated, then the dequeue will return a nil value. This tells the program that for the given indexPath there is NO cell to be displayed.
So, if you get a nil value from the dequeue function, your table will try to create a new cell with the given cell identifier. So, I would suggest you do something like the following:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier;
if(indexPath.section == 0){
cellIdentifier = episodeScrollersCell;
}else if (indexPath.section == 1 && indexPath.row == 0){
cellIdentifier = addCommentsCell;
}else{
cellIdentifier = commentCell;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell){
if(indexPath.section == 0){
//Initialise a new cell here, through a NIB or code
//For eg. cell = [[UITableViewCell alloc] initWithStyle: ...];
}
//Do the specific initialisation for each type of cell you need here
}
return cell;
}
This way, when the dequeue function returns a NIL value, your program automatically creates a new cell to fit in that place.
Hope this helps.
Related
I want to have multiple rows in the first prototype cell; the second cell must be static.
I tried to do it but I cant have the second prototype cell to be static. I am using a custom class for the cell:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ss"];
if (cell.tag == 0)
return 8;
else if (cell.tag == 1)
return 1;
else
return 1;
}
Only the first one gets a return, and I do not even see these two buttons. What am I doing wrong?
Firstly, unless you specify your UITableView to use static cells , you cannot use them. And you cannot mix prototype cells and static cells in single tableview.
According to your requirement, you should create two prototype cells instead of going for one cell as a static cell:
One Prototype -> for your dynamic cells
Second Prototype -> for your static cell
Now change the TableView Datasource as follows :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// suppose [self.dynamicArray count] = 8
//Adding 1 for your static cell
return [self.dynamicArray count] + 1;
}
Suppose, you want to display the last cell with the second Prototype (static cell representation) then implement cellForRowAtIndexPath as :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row < [self.dynamicArray count] )
{
// normal dynamic logic here
NSString *cellIdentifier = #"DynamicCellID"
// dequeue and configure for [self.dynamicArray objectAtIndex:indexPath.row]
}
//this will be the last cell in which you want the second prototype cell acting as Static cell
else
{
// dequeue and configure my second prototype cell for indexPath.row
NSString *cellIdentifier = ... // id for one of my static cells
}
}
You can use the same logic for implementing different heights for the two prototype cells in heightForRowAtIndexPath method.
I did like this
static NSString *cellIdentifier;
if(indexPath.section == 8)
{
cellIdentifier = #"Intake";
}
else
cellIdentifier =#"CollegeDetail";
CollegeDetailCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell==nil) {
cell=[[CollegeDetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
I have a UITablViewController and implementing the following :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tableView cellForRowAtIndexPath:self.oldIndexPath].accessoryType = UITableViewCellAccessoryNone;
NSLog(#"%#", indexPath);
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
self.oldIndexPath = indexPath;
}
What I notice is, the checkmark appears on 2 cells, one with index path, for example, if I select a cell with index path {length = 2, path = 0 - 2} and scrolling down I notice the checkmark is also visible on {length = 2, path = 0 - 14}
There is just 1 section in the table view and around 18-19 rows.
Can somebody verify this for me / or is it time for me to get some sleep :/ ?
P.S : tested this on simulator and device
The cells are created as follows :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
cell.textLabel.text = self.stationsDictionary[#"urls"][indexPath.row][#"title"];
return cell;
}
Since your cells are reusable (I suppose), you need to set this in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tableView cellForRowAtIndexPath:self.oldIndexPath].accessoryType = UITableViewCellAccessoryNone;
NSLog(#"%#", indexPath);
self.oldIndexPath = indexPath;
for (UITableViewCell *cell in tableView.visibleCells) {
if ([tableView indexPathForCell:cell].section == indexPath.section && [tableView indexPathForCell:cell].row == indexPath.row) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
}
and
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
cell.textLabel.text = self.stationsDictionary[#"urls"][indexPath.row][#"title"]; return cell;
cell.accessoryType = self.oldIndexPath.section == indexPath.section && self.oldIndexPath.row == indexPath.row ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
}
Or the reused cell will keep the state of the previous cell (the one reused) which can be checked.
Common error ;)
When you dequeue a cell from cellForRowAtIndexPath, be sure to reset its status - in your case, the accessoryType property.
Since cells are reused, what happens is that when cell with index 2 is not visible, it is reused to display row with index 14.
As a consequence of that, you need to save each cell state if you want to preserve its status (in your case in didSelectRowAtIndexPath if you only need to save the accessoryType property), and restore into the cell in cellForRowAtIndexPath.
You can use an NSArray, using the cell index as array index, to save your cell status, or even a "normal" array of UITableViewCellAccessoryType or booleans
I am using UITableView with group-type, and I am adding different content on UITableViewCell
and each indexPath.section I am checking in UITableViewCell delegate method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cellIdentifier = (indexPath.row % 2 == 0 ? #"EvenCell" : #"OddCell");
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier];
if (indexPath.section == 0)
{
// adding image
// called
}
else if (indexPath.section == 1)
{
// adding lable
called
}
else if (indexPath.section == 2)
{
// adding panorma gps
called
}
else if (indexPath.section == 3)
{
not called // this section problem
}
else if (indexPath.section == 4)
{
not called // this section problem
}
}
return cell;
}
I have given number of section 5, but it is calling only indexPath.section 0, 1, and 2 only.
The problem is that that you only have only two types of cellIdentifier values (i.e. two types of cells you're reusing), but you appear to have five different layouts. The problem is that layouts are only called if a reused cell was not found. Thus, by the time you get down to section 3, dequeueReusableCellWithIdentifier is probably succeeding and reusing a cell that has scrolled off screen, and thus your code inside the if (cell == nil) block is not getting called at all for sections 3 and 4.
You can remedy that by either having more cell types, unique to the section number, too, e.g.:
NSString *cellType = (indexPath.row % 2 == 0 ? #"EvenCell" : #"OddCell");
NSString *cellIdentifier = [NSString stringWithFormat:#"%#-%d", cellType, indexPath.section];
You haven't shared what the even/odd logic is used for, so I'm not entirely sure what that's needed for. Assuming it's for something simple like backgroundColor, then I might be inclined to drop even/odd from the cellIdentifier altogether (and move the setting the background color outside the if (cell==nil) block), and just use the section number for the cell identifier:
NSString *cellIdentifier = [NSString stringWithFormat:#"MyCellSection-%d", indexPath.section];
We'd need a better sense of how different the various cells are configured (what is the degree of commonality) to make concrete suggestions on the best way to format this. But the key issue is that your cellIdentifier choices have to correspond to what's inside the if (cell == nil) block.
make sure you are returning 5 from here
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 5;
}
You call a method to the number of sections as follows?
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 5;
}
Try call your methods after if(cell == nil):
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier];
}
if (indexPath.section == 0) {
// adding image
// called
}
else if (indexPath.section == 1) {
// adding lable
called
}
else if (indexPath.section == 2) {
// adding panorma gps
called
}
else if (indexPath.section == 3) {
not called // this section problem
}
else if (indexPath.section == 4) {
not called // this section problem
}
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;
}
I am trying to aggregate data from three social networks (Facebook,LinkedIn,Twitter). I have all the appropriate and correct feeds and I also have difference cell types for them.
The question i want to ask is, how can i make a UITableView, containing 10 sections with 3 cells(plus three different cell types) per section in this order
Section 1:
[Facebook cell index 0 of the feed array]
[Twitter cell index 0 of the feed array]
[LinkedIn cell index 0 of the feed array]
Section 2:
[Facebook cell index 1 of the feed array]
[Twitter cell index 1 of the feed array]
[LinkedIn cell index 1 of the feed array]
Section 3:
etc etc etc
Play with the table view's data source & delegate. What's important is to use 3 different cell identifiers for 3 types of cells (unless you want them to have same look).
-numberOfSectionsInTableView: {
return 10;
}
–tableView:numberOfRowsInSection: {
return 3;
}
-tableView:cellForRowAtIndexPath:(NSIndexPath*)indexPath {
if (indexPath.row == 0) {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"FacebookCell"];
if (cell == nil) {
// Init FB cell here
}
// Load FB feed data into the cell here
return cell;
}
else if (indexPath.row == 1) {
// Twitter Cell, remember to user a different cell identifier
}
else ...
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 10;
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * cellIdentifier = #"cellId";
customCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(indexPath.row == 0)
{
cell.textLabel.text = [FbFeed objectAtIndex:indexpath.section];
// set FacebookCell
cell
}
else if (indexPath.row == 1)
{
// set TwitterCell
cell.textLabel.text = [tweetFeed objectAtIndex:indexpath.section];
}
else if (indexPath.row ==2)
{
cell.textLabel.text = [linkedinFeed objectAtIndex:indexpath.section];
//set linkedin
}
return cell;
}
To build off the example, this can be used for any multiple type cells. You don't always have to use the row number to decide the type. You can get an object at the row and decide what type to show.
Also, if you are using storyboard, just add another prototype cell in the table, assign it a unique identifier, and set it up so you can use it. Works really well if you need different layouts that depend on the data returned.
-tableView:cellForRowAtIndexPath:(NSIndexPath*)indexPath {
//Check what cell type it is. In this example, its using the row as the factor. You could easily get the object and decide from an object what type to use.
if (indexPath.row == 0) {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"Type1Cell"];
return cell;
}
else if (indexPath.row == 1) {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"Type2Cell"];
return cell;
}
else {
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"Type3Cell"];
return cell;
}
}