I'm creating a recipes app and have run into this semantic issue in the number of rows in section method of my table view. This is the first time I've really worked with table Views and I'm wondering if someone could perhaps see what I've done wrong and point me in the right direction. Thanks!
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (menuInt == 0)
return [soupsArray count];
if (menuInt == 1)
return [saladsArray count];
if (menuInt == 2)
return [appetizersArray count];
if (menuInt == 3)
return [entreeArray count];
if (menuInt == 4)
return [dissertsArray count];
[self.tableView reloadData];
}
What happens if all the if conditions fail? You need to make sure that you at least return an NSInteger even though you are sure one of the if conditions will succeed for sure. That's just how it is.
Also, as Martin R pointed out, you should not have reloadData in the function.
try one of these 2 solutions (the reload data must occur before the return statement also)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int counter = 0;
// Return the number of rows in the section.
if (menuInt == 0)
counter = [soupsArray count];
if (menuInt == 1)
counter = [saladsArray count];
if (menuInt == 2)
counter = [appetizersArray count];
if (menuInt == 3)
counter = [entreeArray count];
if (menuInt == 4)
counter = [dissertsArray count];
[self.tableView reloadData];
return counter;
}
or
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (menuInt == 0) {
[self.tableView reloadData];
return [soupsArray count];
}
else if (menuInt == 1) {
[self.tableView reloadData];
return [saladsArray count];
}
else if (menuInt == 2) {
[self.tableView reloadData];
return [appetizersArray count];
}
else if (menuInt == 3) {
[self.tableView reloadData];
return [entreeArray count];
}
else if (menuInt == 4) {
[self.tableView reloadData];
return [dissertsArray count];
else {
[self.tableview reloadData];
return 0;
}
}
Related
I'm getting this error when adding a row to a UITable.
'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 7. The number of rows contained in an existing section after the update (0) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
number of elements:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([self tableView:tableView canCollapseSection:section])
{
if ([expandedSections containsIndex:section])
{
return mArray[section];
}
return 1; // only top row showing
}
// Return the number of rows in the section.
return 1;
}
Counting number to be in each part:
int k = 0;
for (int i = 0; i < 28; i++) {
if ([[currentEntry objectForKey:#"item"] isEqualToString:items[i]]) {
mArray[i] += 1;
k = i;
}
}
Adding to table
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self tableView:tableView canCollapseSection:indexPath.section])
{
if (!indexPath.row)
{
// only first row toggles exapand/collapse
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSInteger section = indexPath.section;
BOOL currentlyExpanded = [expandedSections containsIndex:section];
NSInteger rows;
NSMutableArray *tmpArray = [NSMutableArray array];
if (currentlyExpanded)
{
rows = [self tableView:tableView numberOfRowsInSection:section];
[expandedSections removeIndex:section];
}
else
{
[expandedSections addIndex:section];
rows = [self tableView:tableView numberOfRowsInSection:section];
NSLog(#"SSection:%d", rows);
}
for (int i=1; i<rows; i++)
{
NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForRow:i
inSection:section];
[tmpArray addObject:tmpIndexPath];
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (currentlyExpanded)
{
dispatch_async(dispatch_get_main_queue(), ^{
[tableView deleteRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationTop];
});
}
Error occurs here
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[tableView insertRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationTop];
});
}
}
}
}
This error only occurs in one section which is 7. All other sections are fine
First you need add object in collection which telling tableview about count of items for appearing, create index path for new cell and make insert row with index path. Perhaps you create wrong index path.
I think your array and your numberOfRows should be matching in your 7th section.
//MARK: UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 7) {
return YOUR_ARRAY.count;
}
}
I have a problem which seems to be very simple. I have a UITableView in my APP. In numberOfSectionsInTableView: I set the count of sections to 2. However the tableView shows only the first section. I tried to make the two sections show same contents by writing same codes in both sections, but useless. Then I found a similar question here. But its correct answer is about frame and my layout is a very simple UITableViewController. At last I tried to add NSLog to the three methods. The result is: For case section 1, numberOfSectionsInTableView: and numberOfRowsInSection: are called but cellForRowAtIndex isn't called. I wonder how to explain this.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
int count;
switch (section) {
case 0:
count = [_objectArray count];
break;
case 1:
NSLog(#"section called"); // This line is logged
count = 1;
default:
count = 0;
break;
}
return count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"myCellIdentifier";
static NSString *basicCellIdentifier = #"myBasicCellIdentifier";
switch ([indexPath section]) {
case 0: {
TableViewStoryCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
// Configure the cell...
int theIndex = [indexPath row];
MyObject *theObj = [_objectArray objectAtIndex:theIndex];
cell.object = theObj;
return cell;
break;
}
case 1: {
NSLog(#"cell called"); //this line isn't logged
for (int i = 0; i < 50; i++) {
NSLog(#"hahaha");
}
MyBasicViewStoryCell *cell = [tableView dequeueReusableCellWithIdentifier:basicCellIdentifier forIndexPath:indexPath];
return cell;
break;
}
default: {
for (int i = 0; i < 50; i++) {
NSLog(#"hahaha1");
}
return nil;
break;
}
}
}
Put break in switch case. You missed break in case 1: so the default will also execute. Thus the count become zero count = 0. Since numberOfRowsInSection: returns zero, cellForRowAtIndexPath: will not call. So put break in case 1: of numberOfRowsInSection:
switch (section) {
case 0:
count = [_objectArray count];
break;
case 1:
NSLog(#"section called"); // This line is logged
count = 1;
break; // Here
default:
count = 0;
break;
}
You don't have a break after you set the count for section 1 and it falls through and is set to zero.
case 1:
NSLog(#"section called"); // This line is logged
count = 1;
break; // always put break!
cellForRowAtIndexPath won't be called if numberOfRowsInSection is not returning a value that is >=1.
That is:
if ([self.tableView numberOfRowsInSection:0] == 0) {
self.tableView.tableFooterView.hidden = NO;
} else {
self.tableView.tableFooterView.hidden = YES;
}
But I hope it can update automatically.
You can implement this function :
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
if ([tableView numberOfRowsInSection:section] == 0) return ***height***;
else return 0;
}
Finally I put the show/hide tableFooterView logic into:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
It works well for me.
I have a table view which has 10000+ cells. and there is a segment button (All/Favorite) on the top.
this is the call back for the segment:
- (IBAction)call_segment:(id)sender {
[self.tableView beginUpdates];
[self.tableView reloadData];
[self.tableView endUpdates];
}
for favorite page, even when there are no favorite items, I simply set the cell height to be 0. But in this way, I created all 10000+ cells on screen.
if 'all' is selected, the table works just fine since cells have normal height and only some of them are dequeued on screen.
Here is my code:
//if it's not in favorite, just hide it by setting the height to be 0
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([self isFavorite]) {
int uniqueId = [self uniqueIdWithIndexPath:indexPath];
if ([DATABASE isFavoriteWithMode:self.mode uniqueId:uniqueId] == NO) {
return 0;
}
}
return 60;
}
//in table view datasource:
//I think the problem is, when setting the height to be 0, all the cells are allocated. I set the cell to be hidden but still takes memory. any way to deal with it?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
BOOL isFavorite = [DATABASE isFavoriteWithMode:self.mode uniqueId:[self uniqueIdWithIndexPath:indexPath]];
if ([self isFavorite] && isFavorite == NO) {
cell.hidden = YES;
return [[UITableViewCell alloc] init];
}
else {
cell.hidden = NO;
ListCell *cell = (ListCell *)[tableView dequeueReusableCellWithIdentifier:CELL_LIST];
Datum *datum = [DATABASE datumWithMode:self.mode uniqueId:[self uniqueIdWithIndexPath:indexPath]];
BOOL isRead = [DATABASE isReadWithMode:self.mode uniqueId:[self uniqueIdWithIndexPath:indexPath]];
cell.indexLabel.text = [NSString stringWithFormat:#"%d", datum.uniqueId];
cell.titleLabel.text = [NSString stringWithFormat:#"%#", datum.q];
return cell;
}
}
Note: I dont wanna just show the favorite cells, since the logic is way too complex. I am using sqlite, but i dont think database performance is the problem, since the 'all' tab works just fine.
The reason i wanted to just set the height to be 0 is the simple implementation of cell numbers
- (BOOL)isFavorite {
return self.segment.selectedSegmentIndex == 1;
}
- (IBAction)call_segment:(id)sender {
[self.tableView beginUpdates];
[self.tableView reloadData];
[self.tableView endUpdates];
}
#define NUM_SECTIONS 15
- (int)numRows {
return [DATABASE numberOfDataForModes:self.mode];
}
- (int)numSections {
if ([self numRows] % NUM_SECTIONS > 0) {
int numSections = [self numRows] / [self numRowsPerSection];
if ([self numRows] % [self numRowsPerSection] > 0) {
numSections++;
}
return numSections;
}
return NUM_SECTIONS;
}
- (int)numRowsPerSection {
return [self numRows] / NUM_SECTIONS;
}
- (int)numRowsInLastSection {
if ([self numRows] % ([self numSections] - 1) > 0) {
return [self numRows] % ([self numSections] - 1);
}
else {
return [self numRowsPerSection];
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
int start = section * [self numRowsPerSection] + 1;
int end = start + [self numRowsPerSection] - 1;
if (end > [self numRows]) {
end = [self numRows];
}
return [NSString stringWithFormat:#"From %d to %d", start, end];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
NSMutableArray *titles = [NSMutableArray arrayWithCapacity:[self numSections]];
int start = 1;
while (start < [self numRows]) {
NSString *title = [NSString stringWithFormat:#"%d", start];
[titles addObject:title];
start += [self numRowsPerSection];
}
return titles;
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return index;
}
- (int)uniqueIdWithIndexPath:(NSIndexPath *)indexPath {
int uniqueId = indexPath.row + 1 + indexPath.section * [self numRowsPerSection];
return uniqueId;
}
- (NSIndexPath *)indexPathWithUniqueId: (int)uniqueId {
int section = (uniqueId - 1) / [self numRowsPerSection];
int row = uniqueId - 1 - [self numRowsPerSection] * section;
return [NSIndexPath indexPathForRow:row inSection:section];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([self isFavorite]) {
int uniqueId = [self uniqueIdWithIndexPath:indexPath];
if ([DATABASE isFavoriteWithMode:self.mode uniqueId:uniqueId] == NO) {
return 0;
}
}
return 60;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == [self numSections] - 1) {
return [self numRowsInLastSection];
}
return [self numRowsPerSection];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self numSections];
}
Instead of hiding the cells why dont you just return 0 from the datasource method
– tableView:numberOfRowsInSection:
You can just make use of the isFavorite value within this function and return 0 if there it is NO.
You got it already. The problem is the size of 0 of non-favorite cells. That contradicts the idea of reusabel cells. You will have thousands of cells on the screen, although invisible but existing and therefore resource consuming. Better think of a smarter way of doing that. Your data source delegate (view controller I guess) should only return the number of non-fav cells and therefore cellForRowAtIndexPath should only provide those cells of non-fav items. Plus cellForRowAtIndexPath should actually reuse the cells which I do not see in your code sniplet.
No matter how much you try having 10,000 views onscreen is not going to be the solution to your problem. You need to change your code structure such that you can return 0 for the tableView:numberOfRowsInSection: delegate when the favourites tab is chosen.
Any other 'solution' is an attempt to hack an alternative together, but this will not work and is bad code practice anyway. Implement it properly, by responding to the delegates properly.
I've given up making both table section separated. the logic is way too complicated.
I guess there is no way to save memory even when you hide the cells. Thank you guys for your input. you are all correct.
It's actually not that bad since favorite table are typically not that long. just one section with all entries.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int count = [entries count];
if (count == 0) {
return kCustomRowCount;
}
return count;
int rowCount;
if (self.isFiltered) {
rowCount = filteredTableData.count;
}
else {
rowCount = allTableData.count;
}
return rowCount;
}
My problem: The first function return count; is needed to fill the parsed data into the tableView. The second one return rowCount; is needed to count the filtered entries for the search. But when I use both, my App dies. When I delete the first part, the search seems to work incorrectly..
Sascha
It sounds like you need to be making use of the UISearchDisplayController. This controller essentially supports an unfiltered and a filtered (searched) list.
You can then use something like the following in your numberOfRowsInSection:
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(tableView == self.searchDisplayController.searchResultsTableView){
// search view population
return [self.filteredList count];
} else {
return [[self.sectionedList objectAtIndex:section] count];
}
}