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
}
Related
I'm trying to set the title and display a button in my custom cell object in my second table view controller, but only my first dynamic prototype cell is displaying text.
The first prototype cell is UITableViewCell and the second is the custom object. In my first view controller I implement almost the same exact method and it works completely fine. Not sure where my error could be, the models I use have all their data in them. I have the correct cell identifier in my storyboard, and the connection from custom cell to the UIButton is set.
Thanks in advance.
Method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CellIdentifier";
static NSString *QuestionSectionIdentifier = #"QuestionSectionCell";
if (indexPath.section == 0)
{
UITableViewCell *cellQuestion = [tableView
dequeueReusableCellWithIdentifier:QuestionSectionIdentifier];
if (cellQuestion == nil) {
cellQuestion = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:QuestionSectionIdentifier];
}
if (indexPath.row == 0) {
cellQuestion.textLabel.text = _headerQuestion.question;
NSLog(#"question here %#", cellQuestion.textLabel.text);
}
return cellQuestion;
}
else
{
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if (indexPath.section == 1)
{
if (indexPath.row == 0)
{
[cell.firstButton setTitle:_headerQuestion.questionAuthor.fullName forState:UIControlStateNormal];
[cell.firstButton setTag:_headerQuestion.questionAuthor.userID];
[cell.answerLabelField setHidden:YES];
NSLog(#"section 1 %#", cell.firstButton.titleLabel.text);
}
}
else if (indexPath.section == 2)
{
Answer* answer_num = [_headerQuestion.answers objectAtIndex:indexPath.row]; //object at index
[cell.firstButton setTitle:answer_num.answerAuthor.fullName forState:UIControlStateNormal];
[cell.firstButton setTag:answer_num.answerAuthor.userID];
cell.answerLabelField.text = answer_num.answer;
}
return cell;
}
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 3;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0)
{
return 1;
}
if (section == 1)
{
return 1;
}
else
{
return [_headerQuestion.answers count];
}
}
Make sure you register both reuse identifiers with the tableview using
- (void)registerClass:(Class nullable)cellClass
forCellReuseIdentifier:(NSString * nonnull)identifier
Or registerNib if you have a nib for the cell.
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.
I have a UITableViewController that is duplicating rows as I scroll down. I understand that the cells are being reused, but I just can't seem to figure out what to do to fix it. I've read through other similar posts and couldn't figure out a solution in my case.
What am I doing wrong in my code that's resulting in duplicated cells?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"StringTableViewCell";
if (indexPath.row == 0) {
cellIdentifier = #"StringTableViewCell";
} else if (indexPath.row == 1) {
cellIdentifier = #"StringTableViewFooter";
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// Sets up the table view cell accordingly based around whether it is the
// string row or the footer
if (cell) {
if ([cell isKindOfClass:[StringTableViewCell class]]) {
StringTableViewCell *stringCell = (StringTableViewCell *)cell;
// Sets the photos to the array of photo data for the collection view
[stringCell setCollectionData:self.images];
} else if ([cell isKindOfClass:[StringFooterTableViewCell class]]) {
// Sets the table view cell to be the custom footer view
StringFooterTableViewCell *footerCell = [[StringFooterTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"StringTableViewCellFooter"];
// Init's the footer with live data from here
[footerCell setStringUploaderName:#"Name"];
[footerCell setStringUploadDate:#"10 minutes ago"];
[footerCell setStringUploaderProfileImage:[UIImage imageNamed:#"avatar.jpg"]];
[footerCell setCommentButtonTitle:#"4.7k"];
[footerCell setLikeButtonTitle:#"11.3k"];
return footerCell;
}
}
return cell;
}
There's a prepareForReuse method for UITableViewCell which is called every time it's about to be reused. You can set your StringTableViewCell's imageview's image to nil there or you can just do it on the cell for indexPath itself, as the prepareForReuse method is only encouraged to be used in resetting the state(like hightlighted, or selected) for the cell and it's subviews.
Since you have two different cells you need something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
static NSString *cellIdentifier = #"StringTableViewCell";
StringTableViewCell *stringCell = (StringTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!stringCell) {
stringCell = [[StringTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
[stringCell setCollectionData:self.images];
return stringCell;
} else if (indexPath.row == 1) {
static NSString *cellIdentifier = #"StringTableViewFooter";
StringFooterTableViewCell *footerCell = (StringFooterTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!footerCell) {
footerCell = [[StringFooterTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
// Init's the footer with live data from here
[footerCell setStringUploaderName:#"Name"];
[footerCell setStringUploadDate:#"10 minutes ago"];
[footerCell setStringUploaderProfileImage:[UIImage imageNamed:#"avatar.jpg"]];
[footerCell setCommentButtonTitle:#"4.7k"];
[footerCell setLikeButtonTitle:#"11.3k"];
return footerCell;
} else {
return nil; // this shouldn't happen but makes the compiler happy
}
}
Of course the code needs to be updated to show the data appropriate to the given section.
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;
}
}
This is the abridged code for my cellForRowAtIndexPath UITableView delegate method:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"blahblahblah"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"blahblahblah"] autorelease];
}
cell.textLabel.text = #"";
cell.detailTextLabel.text = #"";
cell.backgroundView = NULL; //problem here
// Initialize cell
//blah blah blah
//now to the good part...
if(indexPath.section == 1) {
cell.backgroundView = deleteButton;
cell.userInteractionEnabled = YES;
cell.textLabel.text = nil;
cell.detailTextLabel.text = nil;
}
else if(indexPath.section == 0) {
NSLog(#"section: %i row: %i", indexPath.section, indexPath.row);
switch (indexPath.row) {
case 0:
cell.textLabel.text = #"foobar";
//more stuff
break;
//lots more cases
default:
break;
}
}
return cell;
}
My problem is that the first cell in section 1 (section 0 has 10 cells, section 1 has only 1 cell) is getting assigned the information that is only supposed to be assigned to cell 0 of the first section. So, instead of getting the deleteButton background and etc, it gets the label title "foobar". I'm not really sure why this is happening, because my if statements are pretty clear. Any ideas?
Edit: setting the backgroundView to NULL causes those cells with text to, when they leave the view, come back without any background. So that isn't a viable solution. Also, the text for the detailTextLabel is still set on the cell that shouldn't have any text.
This is how it looks, with the cell backgroundViews set to nil and the text showing up on the delete cell where it shouldn't:
Solution, as recommended by Alex Deem (replacing old dequeue code with this code):
NSString* identifier;
if(indexPath.section == 0)
identifier = #"0";
else
identifier = #"1";
UISwitch *switchView;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier] autorelease];
You should read the documentation regarding the reuse of cells.
You should be using a different reuseIdentifier for each of the two sections, since they are fundamentally differently styled cells.
add a closing } bracket to these lines of code:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"blahblahblah"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"blahblahblah"] autorelease];
}
....
....
....
if(indexPath.section == 1) {
....
....
}
if(indexPath.section == 0) {
....
....
}
and I suspect you will have better results in your table.
The way things are working right now (at least as far as I can tell in your code), you create one cell and it gets initialized to something. It never gets reset to the values & data of anything else that's being requested.
Your code is structured wrong.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"blahblahblah"];
if (cell == nil)
dqueueReuseableCellWithIdentifier will return an existing previously created cell if it is no longer in use. If cell == nil you should create a new cell and set defaults common to all cells. However any setting of data unique to that indexPath should be done after the
if(cell==nil) block.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"blahblahblah"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"blahblahblah"] autorelease];
}
cell.textLabel.text=#"unique text";
return cell;