iOS UITableView reusable cell slow when showing all of them - ios

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.

Related

UISearch Bar in ios 8

I am getting JSONArray data and show phone numbers in table
I am using UISearchBar in UITableView.
When I am type in search bar it reload data and show type value in first cell.
But i clicked first cell means it show old array value.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (isSearching)
{
cell.textLabel.text = [searchResultArray objectAtIndex:indexPath.row];
}
else
{
cell.textLabel.text = [phoneNoArray objectAtIndex:indexPath.row];
}
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
[searchResultArray removeAllObjects];
if([searchText length] != 0)
{
isSearching = YES;
[self searchTableList];
}
else
{
isSearching = NO;
}
[self.tableView reloadData];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
tempString = [jsonArray objectAtIndex:indexPath.row];
}
when you set isSearching to yes. Now datasoucre of table has changed. and you have reloaded the data.
and you are supposed to update your datasource methods as well
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (isSearching) {
return [searchResultArray count];
}
else
{
return [phoneNoArray count];
}
}
HTH

iOS - Expand/Collapse UITableView section with animations

I want to expand/collapse my UITableView sections with animations. I used this answer and it works now if I call self.tableView.reloadData(). But I want that when I tap on my custom UITableView- header , the cells of the section should slide down/up with a nice animation. I tried to use self.tableView.beginUpdates() and self.tableView.endUpdates(), but I get this error:
Invalid update: invalid number of rows in section 0. The number of rows contained in an
existing section after the update (8) must be equal to the number of rows contained in that
section before the update (0), 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).
Here's some code. The method that is called when I tap on the section:
func expand(sender:UITapGestureRecognizer){
let tag = (sender.view?.tag)!
self.tableView.beginUpdates()
if ausgeklappt[tag] { ausgeklappt[tag] = false }
else { ausgeklappt[tag] = true }
self.tableView.endUpdates()
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
let keyDerSection = sortSpieleDict.keys.array[section]
let arrayDerSection = sortSpieleDict[keyDerSection]!
if ausgeklappt[section] == false { return 0 }
else { return arrayDerSection.count }
}
Thanks.
Thanks to iOS_DEV I found a solution:
Just one line of code did the trick. I just replaced the beginUpdates() and endUpdates() with the reloadSections() method. Now it works fine!
func expand(sender:UITapGestureRecognizer){
let tag = (sender.view?.tag)! // The tag value is the section of my custom UITabelView header view.
if ausgeklappt[tag] { ausgeklappt[tag] = false }
else { ausgeklappt[tag] = true }
// The next line did the trick!
self.tableView.reloadSections(NSIndexSet(index: tag), withRowAnimation: UITableViewRowAnimation.Automatic)
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
let keyDerSection = sortSpieleDict.keys.array[section]
let arrayDerSection = sortSpieleDict[keyDerSection]!
if ausgeklappt[section] == false
{
return 0
}
else
{
return arrayDerSection.count
}
}
i used a NSMutableSet to keep track of clicked headers. I placed a UIButton on every header which responded to the following event:
#pragma mark - Header Clicked
-(void) headerClicked:(UIButton *) sender{
if ([set_OpenIndex containsObject:[NSNumber numberWithUnsignedInteger:sender.tag]]) {
[set_OpenIndex removeObject:[NSNumber numberWithUnsignedInteger:sender.tag]];
[tableview_Main reloadSections:[NSIndexSet indexSetWithIndex:sender.tag] withRowAnimation:UITableViewRowAnimationAutomatic];
}
else{
if (set_OpenIndex.count > 0) {
//--- a header is opened already, close the previous one before opening the other
[UIView animateWithDuration:0.5 animations:^{
[set_OpenIndex enumerateObjectsUsingBlock:^(id obj, BOOL *stop){
[set_OpenIndex removeObject:obj];
[tableview_Main reloadSections:[NSIndexSet indexSetWithIndex:[obj integerValue]] withRowAnimation:UITableViewRowAnimationAutomatic];
}];
} completion:^(BOOL finished){
[set_OpenIndex addObject:[NSNumber numberWithUnsignedInteger:sender.tag]];
[tableview_Main reloadSections:[NSIndexSet indexSetWithIndex:sender.tag] withRowAnimation:UITableViewRowAnimationAutomatic];
}];
}
else{
[set_OpenIndex addObject:[NSNumber numberWithUnsignedInteger:sender.tag]];
[tableview_Main reloadSections:[NSIndexSet indexSetWithIndex:sender.tag] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
}
And just set up the number of rows as follows:
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if ([set_OpenIndex containsObject:[NSNumber numberWithInteger:section]]) {
return 5; // or what ever is the number of rows
}
return 0;
}
I have written this code in Objective-C as i don't have much knowledge of swift yet. This is just for the logic. Please convert the code according to your need.
Note: don't forget to set the tag of the UIButton in header according to the section number.
Basically tableview beginupdates & endplates is used when there is a change in the section-row model and we update delete or insert few rows.
However your problem must be from the cellforHeight or cellforRowAtIndex where you need to put the section in switch case.
Thanks
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initialization];
}
#pragma mark - Initialization
-(void)initialization
{
arrayForBool=[[NSMutableArray alloc]init];
dic_data =[[NSMutableDictionary alloc]init];
[dic_data setValue:#[#"1",#"2"] forKey:#"Section"];
[dic_data setValue:#[#"1",#"2",#"3",#"4",#"5",#"6",#"7",#"8"] forKey:#"Section1"];
[dic_data setValue:#[#"1",#"2",#"3",#"4"] forKey:#"Section2"];
for (int i=0; i<dic_data.allKeys.count; i++) {
[arrayForBool addObject:[NSNumber numberWithBool:NO]];
}
}
#pragma mark -
#pragma mark TableView DataSource and Delegate Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([[arrayForBool objectAtIndex:section] boolValue]) {
NSString *str =[dic_data.allKeys objectAtIndex:section];
return [[dic_data valueForKey:str]count];
}
else
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellid=#"hello";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellid];
if (cell==nil) {
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellid];
}
NSString *str =[dic_data.allKeys objectAtIndex:indexPath.section];
cell.textLabel.text = [[dic_data valueForKey:str]objectAtIndex:indexPath.row];
return cell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return dic_data.allKeys.count;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
/*************** Close the section, once the data is selected ***********************************/
[arrayForBool replaceObjectAtIndex:indexPath.section withObject:[NSNumber numberWithBool:NO]];
[_expandableTableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[arrayForBool objectAtIndex:indexPath.section] boolValue]) {
return 40;
}
return 0;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 40;
}
#pragma mark - Creating View for TableView Section
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *sectionView=[[UIView alloc]initWithFrame:CGRectMake(0, 0, 280,40)];
sectionView.tag=section;
UILabel *viewLabel=[[UILabel alloc]initWithFrame:CGRectMake(10, 0, _expandableTableView.frame.size.width-10, 40)];
viewLabel.backgroundColor=[UIColor clearColor];
viewLabel.textColor=[UIColor blackColor];
viewLabel.font=[UIFont systemFontOfSize:15];
NSString *str =[dic_data.allKeys objectAtIndex:section];
viewLabel.text=[NSString stringWithFormat:#"List of %#",str];
[sectionView addSubview:viewLabel];
/********** Add UITapGestureRecognizer to SectionView **************/
UITapGestureRecognizer *headerTapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(sectionHeaderTapped:)];
[sectionView addGestureRecognizer:headerTapped];
return sectionView;
}
#pragma mark - Table header gesture tapped
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)gestureRecognizer{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:gestureRecognizer.view.tag];
if (indexPath.row == 0) {
BOOL collapsed = [[arrayForBool objectAtIndex:indexPath.section] boolValue];
for (int i=0; i<dic_data.allKeys.count; i++) {
if (indexPath.section==i) {
[arrayForBool replaceObjectAtIndex:i withObject:[NSNumber numberWithBool:!collapsed]];
}
}
[_expandableTableView reloadSections:[NSIndexSet indexSetWithIndex:gestureRecognizer.view.tag] withRowAnimation:UITableViewRowAnimationTop];
}
}
I have written this code in Objective-C as i don't have much knowledge of swift yet. This is just for the logic. Please convert the code according to your need.

Static Cells - Hiding using the heightforRowatIndexPath method

Hi guy I'm trying to hide some of the rows in section 1 (Second section) depending on what type of feedback the user has selected:
I'm using static cells but at the moment nothing is being removed when I select one of the options in the TypeVC. There are no errors at the moment but having a guess I think its something to do with the logical operators I'm using in the switch statement. Sorry for dumping my code but as I'm very new to IOS I don't know what exactly you guys would need to see.
[1]
if (variable == (1|2|3)){}
I'm used to Java and I use this kind statement quite frequently as it saves writing. Is this how to do it in objective-c?
[2]
Where and how have I gone wrong here trying to get the cells to dissapear?
FeedbackTableViewController:
#import "FeedbackTableViewController.h"
#import "TypeTableViewController.h"
#interface FeedbackTableViewController ()
#property NSInteger index;
#end
#implementation FeedbackTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated{
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (NSIndexPath *) tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Type: %i",_type);
if (indexPath.section == 0 && indexPath.row == 0)
[self performSegueWithIdentifier:#"showTypeVC" sender:self];
return indexPath;
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
TypeTableViewController *tvc = [segue destinationViewController];
tvc.indexchoosen = _index;
}
//- (UITableViewCell *)tableView:(UITableView *)tableView
// cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//
// UITableViewCell * cell = [tableView cellForRowAtIndexPath:indexPath];
//
// if (indexPath.row==0) cell.textLabel.text = _typeString;
// else if (indexPath.row)
//
// return cell;
//}
- (CGFloat) tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSLog(#"section: %i row:%i",indexPath.section, indexPath.row);
if (indexPath.section == 1) {
switch (_type) {
case 0:
if (indexPath.row==(2|3|4))return 0;
break;
case 1:
if (indexPath.row==(0|1|4))return 0;
break;
case 2:
if (indexPath.row==(0|1|2|3))return 0;
break;
case 3:
return 0;
break;
case 4:
return 0;
break;
case 5:
return 0;
break;
default:
return 0;
break;
}
}
return 43;
}
- (IBAction)unwindtypeVC:(UIStoryboardSegue *)segue { }
#end
TypeTableViewController:
#import "TypeTableViewController.h"
#import "FeedbackTableViewController.h"
#interface TypeTableViewController ()
#end
#implementation TypeTableViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_typeoptions = #[#"Routing Issues",
#"Wrongly Labelled Location",
#"Missing Location",
#"Comment on Useability",
#"Suggestions",
#"General Feedback"];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 6;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.textLabel.text = self.typeoptions[indexPath.row];
return cell;
}
- (NSIndexPath *)tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
_indexchoosen = indexPath.row;
return indexPath;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
NSString *string = _typeoptions[_indexchoosen];
FeedbackTableViewController *fvc1 = [segue destinationViewController];
fvc1.typeString.text = _typeoptions[_indexchoosen];
fvc1.type = _indexchoosen;
}
#end
I'm open to better ideas on achieving what I want to achieve also so if you would consider telling me a more efficient way of doing this I would be grateful. I know delegates might be an option however I'm not confident with them yet and would thought this would be easier for me.
For [1], try this and see it yourself:
int test = 3;
if(test == (1 | 2))
NSLog(#"_MEH_");
Since it's bitwise OR operation, 0010 | 0001 equals to 0011, which is equal to 3. Hence, I would not advise you to use an operation like that. (If that's not intentional, of course).
For [2], you should use deleteRowsAtIndexPaths:withRowAnimation: call for UITableView in order to delete rows.
For example;
[self.tableView beginUpdates];
NSIndexPath* rowToDelete = [NSIndexPath indexPathForRow:0 inSection:0]; // For showing purposes only.
NSArray* indexArray = [NSArray arrayWithObjects:rowToDelete, nil];
[self.tableView deleteRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationMiddle];
[self.tableView endUpdates];
Also, don't forget to update your data source accordingly. You may want to remove some objects from
self.typeoptions
array.
P.S: Another addition, you should also change tableView:numberOfRowsInSection: since there will be less rows than 6.
I actually managed to use this method of changing the row heights to 0.
In order to do it effectively I had to to remove the placeholder/any initial text in the rows that I didn't want shown. This required some storyboard connections which you will see named as _feedbackText _startLocation etc. When a user selected a new row, they would perform a segue to the original feedback form and therefore the viewDidAppear was called. I used this to call the [self.tableView reloadData]. Originally the change in the variable _type would not actually change anything but the heightForRowAtIndexPath is recalled when the data is reloaded.
I'm sure that using the delete row at indexPath would have worked also but I wanted to store the information that the user may have typed before they changed the type of feedback.
The New Method:
- (CGFloat) tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath{
NSInteger i = indexPath.row;
if (indexPath.section == 1) {
switch (_type) {
case 0:
_startLocation.placeholder = #"Start Location:";
_destination.placeholder = #"Destination:";
_locationName.placeholder = #"";
_correctName.placeholder = #"";
_missingLocation.placeholder = #"";
if (i==2||i==3||i==4) return 0;
break;
case 1:
_startLocation.placeholder = #"";
_destination.placeholder = #"";
_locationName.placeholder = #"Location Name:";
_correctName.placeholder = #"Correct Name:";
_missingLocation.placeholder = #"";
if (i==0||i==1||i==4)return 0;
break;
case 2:
_startLocation.placeholder = #"";
_destination.placeholder = #"";
_locationName.placeholder = #"";
_correctName.placeholder = #"";
_missingLocation.placeholder = #"Missing Location:";
if (i==0||i==1||i==2||i==3)return 0;
break;
case 3:
return 0;
break;
case 4:
return 0;
break;
case 5:
return 0;
break;
default:
_startLocation.placeholder = #"";
_destination.placeholder = #"";
_locationName.placeholder = #"";
_correctName.placeholder = #"";
_missingLocation.placeholder = #"";
if (i==0||i==1||i==2||i==3||i==4)return 0;
break;
}
} else if (indexPath.section==2 && indexPath.row==2) return 240;
else if (indexPath.section==0 && indexPath.row==0) return 40;
return 30;
}
This will essentially hide but not get rid of the information in the text fields. This is very useful if you want to the keep any information the user typed in.
I hope this helps anyone trying to hide rows in a grouped static table view controller.

numberOfSectionsInTableView for 2 different cells in the same controller

I have a view that has 2 independent cells. Everything is working properly, except the numberOfRowsInSection return that must be different for each cell.
I want my cell "dummy" to only return 1 or 2.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//I need to add some code in here that will say that
//if my cell = dummy then return 1 else
//Returning table count based on number of Team Names
return _teamNames.count;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0 && indexPath.row == 0) {
return indexPath.row + 20 - indexPath.row;
}
if (indexPath.section == 0 && indexPath.row == 18) {
return indexPath.row + 50 - indexPath.row;
} else {
return indexPath.row + 26 - indexPath.row;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *dummycell = #"dummy";
dummytest *cellar = [tableView dequeueReusableCellWithIdentifier:dummycell
forIndexPath:indexPath];
cellar.righthere.text = #"hello";
static NSString *CellIdentifier = #"StandingsIdent";
StandingsViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
Any ideas? Thank you.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// I need to add some code in here that will say that if my cell = dummy then return 1 else
// You need to set this bool value in your code somewhere - where you have some sort of toggle button between - Dummycell and StandingsViewCell
if(isDummyCell)
{
return 1;
}
else
{
return _teamNames.count; // Returning table count based on number of Team Names
}
}
You can use a NSMutableDictionary as source for your Table View data (i.e. "MyData"), so you can control the behaviour of every cell directly by the data source.
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
//list of elements
if (self.keys.count == 0) {
return 0;
}
NSString *key = [self.keys objectAtIndex:section];
NSArray *mySection = [MyData objectForKey:key];
return [mySection count];
}
where keys is the array that holds your sections. When you load your data, if you want an alphabetical order, you've only to do:
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
[keyArray addObjectsFromArray:[[self.MyData allKeys] sortedArrayUsingSelector:#selector(compare:)]];
self.keys = keyArray;

Assertion Failure when updating uitableview

I get this assertion when trying to update my tableview when clicking on a section header.
* Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2839.5/UITableView.m:1264
I am just trying to hide and show custom cells whenever I click on a section header view.
code works fine if I replace the update code with reload data. but that's not smooth :(
- (void)noteSectionHeader:(UTNoteSectionHeader *)noteSectionHeader sectionTapped:(NSInteger)section
{
UTNoteItem* noteItem = self.notes[section];
BOOL alreadySelected = noteItem.selected;
if (alreadySelected) {
self.selectedSection = NSNotFound;
[self setSelected:NO forSection:section];
}
else {
self.selectedSection = section;
[self setSelected:YES forSection:section];
}
[self updateSections];
}
- (void)setSelected:(BOOL)selected forSection:(NSInteger)section
{
UTNoteItem* noteItem = self.notes[section];
noteItem.selected = selected;
for (UTNoteItem* tmpItem in self.notes) {
if (tmpItem != noteItem) {
tmpItem.selected = NO;
}
}
}
- (void)updateSections
{
NSMutableArray* deletePaths = [[NSMutableArray alloc] init];
NSMutableArray* addPaths = [[NSMutableArray alloc] init];
for (UTNoteItem* item in self.notes) {
if (item.selected) {
[addPaths addObject:[NSIndexPath indexPathForRow:0 inSection:[self.notes indexOfObject:item]]];
}
else {
[deletePaths addObject:[NSIndexPath indexPathForRow:0 inSection:[self.notes indexOfObject:item]]];
}
}
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:deletePaths withRowAnimation:YES];
[self.tableView insertRowsAtIndexPaths:addPaths withRowAnimation:YES];
[self.tableView endUpdates];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return self.notes.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
UTNoteItem* itemNote = self.notes[section];
if (itemNote.selected) return 1;
return 0;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 40;
}
EDIT:
Here is my new implementation:
-
(void)noteSectionHeader:(UTNoteSectionHeader *)noteSectionHeader sectionTapped:(NSInteger)section
{
/* Check if a section is opened */
if (self.selectedSection != NSNotFound) {
/* A section is open, get the item */
UTNoteItem* theItem = self.notes[self.selectedSection];
/* if the item is the section opened, close it */
if (self.selectedSection == section) {
theItem.selected = NO;
self.selectedSection = NSNotFound;
}
/* The item is not the section, so open it, and close the previous item */
else {
theItem.selected = YES;
UTNoteItem* prevItem = self.notes[self.selectedSection];
prevItem.selected = NO;
self.selectedSection = section;
}
}
/* Nothin is open, just open the section */
else {
self.selectedSection = section;
UTNoteItem* openItem = self.notes[self.selectedSection];
openItem.selected = YES;
}
/* Reload the selected section.. this will not reload the other sections? */
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationAutomatic];
}
I have had a similar problem, however I perform a reload like so:
- (void)noteSectionHeader:(UTNoteSectionHeader *)noteSectionHeader sectionTapped:(NSInteger)section
{
//check our action
if(<will hide section>) {
//hide section
<some action>
} else {
//show section
<some action>
}
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic];
}
and it reloads again differently with a forced update:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger nRows = 0; //no rows when closed
if(<check section is open>) {
nRows +=<number of data items shown>;
}
return nRows;
}
Where the indexpath.section is the section I wish to hide or show. it is smooth and stable.
Deleting and adding rows is a little dangerous in my opinion, tableviews are very good at doing animated reloads on individual sections or cells.

Resources