Expanding and collapsing tableView Sections - ios

I want to expand/collapse table view sections.I googled and found a code and its working fine.but the problem is the previously opened section is not closed when a new section is opened.Thanks
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)gestureRecognizer{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:gestureRecognizer.view.tag];
if (indexPath.row == 0) {
BOOL collapsed = [[arrayForBool objectAtIndex:indexPath.section] boolValue];
collapsed = !collapsed;
[arrayForBool replaceObjectAtIndex:indexPath.section withObject:[NSNumber numberWithBool:collapsed]];
//reload specific section animated
NSRange range = NSMakeRange(indexPath.section, 1);
NSIndexSet *sectionToReload = [NSIndexSet indexSetWithIndexesInRange:range];
[self.aTableView reloadSections:sectionToReload withRowAnimation:UITableViewRowAnimationFade];
}
}
I tried the following code which is working but the animation is not cool as all the sections are reloading.
NSMutableArray *isSectionTouched =[[NSMutableArray alloc]initWithCapacity:arrayForBool.count];
isSectionTouched=[arrayForBool mutableCopy];
for(int i = 1; i <[arrayForBool count] ; i ++){
if(i != gestureRecognizer.view.tag){
[isSectionTouched replaceObjectAtIndex:i withObject:[NSNumber numberWithBool:NO]];
}else{
if ([[isSectionTouched objectAtIndex:gestureRecognizer.view.tag]boolValue]==YES) {
[isSectionTouched replaceObjectAtIndex:gestureRecognizer.view.tag withObject:[NSNumber numberWithBool:NO]];
}else if ([[isSectionTouched objectAtIndex:gestureRecognizer.view.tag]boolValue]==NO){
[isSectionTouched replaceObjectAtIndex:gestureRecognizer.view.tag withObject:[NSNumber numberWithBool:YES]];
}
}
}
arrayForBool=isSectionTouched;
NSRange range = NSMakeRange(1,arrayForBool.count - 1);
NSIndexSet *sectionToReload = [NSIndexSet indexSetWithIndexesInRange:range];
[self.tableView reloadSections:sectionToReload withRowAnimation:UITableViewRowAnimationFade];

Just before you replace the selected index path, iterate over arrayForBool and set each item to NO. You could hold a property to store the currently open section index but unless you have hundreds of sections it isn't worth it.

Related

update UITableView sectionHeaderHeight at time of UIButton click in iOS

Good day! My default tableview sectionHeaderHeight is 56. I want to update it when sectionOpened method gets called.This is the following code:
- (void) sectionOpened : (NSInteger) section
{
sectionHeight = #"YES";
getsection = section;
NSLog(#"Section Clicked: %ld",(long)getsection);
SectionInfo *array = [self.sectionInfoArray objectAtIndex:section];
array.open = YES;
NSInteger count = [array.category.menulist count];
NSMutableArray *indexPathToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i<count;i++)
{
[indexPathToInsert addObject:[NSIndexPath indexPathForRow:i inSection:section]];
}
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
NSInteger previousOpenIndex = self.openSectionIndex;
if (previousOpenIndex != NSNotFound)
{
SectionInfo *sectionArray = [self.sectionInfoArray objectAtIndex:previousOpenIndex];
sectionArray.open = NO;
NSInteger counts = [sectionArray.category.menulist count];
[sectionArray.sectionView toggleButtonPressed:FALSE];
for (NSInteger i = 0; i<counts; i++)
{
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:previousOpenIndex]];
}
}
UITableViewRowAnimation insertAnimation;
UITableViewRowAnimation deleteAnimation;
if (previousOpenIndex == NSNotFound || section < previousOpenIndex)
{
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationBottom;
}
else
{
insertAnimation = UITableViewRowAnimationBottom;
deleteAnimation = UITableViewRowAnimationTop;
}
[menulistTable beginUpdates];
if(section==3 || section==4 || section==5) // this section has cells like submenu of a menu
{
menulistTable.sectionHeaderHeight = 20.0;
[menulistTable reloadSections:[NSIndexSet indexSetWithIndex:section] withRowAnimation:UITableViewRowAnimationNone];
//[menulistTable reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
}
[menulistTable insertRowsAtIndexPaths:indexPathToInsert withRowAnimation:insertAnimation];
[menulistTable deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
[menulistTable endUpdates];
self.openSectionIndex = section;
}
But nothing happening. I also used indexSetWithIndex:0. Whats wrong in the code?
Try like this to change Height:
[menulistTable beginUpdates];
menulistTable.sectionHeaderHeight = 20.0;
[menulistTable endUpdates];
Edited:
Take variable to set SectionHeaderHeight. Then place this code:
[menulistTable beginUpdates];
secHeaderHeight = 20.0;
[menulistTable endUpdates];
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return secHeaderHeight;
}
Hope it will work for you.

ios tableView reloadRowsAtIndexPaths not working

I have an issue where I have a UITableViewController and when the view appears I do some calculations asynchronously which should result in the updating of specific rows in the table.
Inside the viewWillAppear function I calculate the necessary rows that need to be updated as follows:
- (void)reloadPaths:(NSMutableDictionary *)bookingsDict
{
NSMutableArray* indexArray = [NSMutableArray array];
int i = 0;
for (NSString *dateKey in self.eventsToShow) {
NSMutableDictionary *venues = [bookingsDict objectForKey:dateKey];
if (venues) {
int j = 0;
NSArray *events = [self.eventsToShow objectForKey:dateKey];
for (DCTEvent *event in events) {
NSString *venueIdString = [NSString stringWithFormat:#"%#", event.eventVenueId];
if ([venues objectForKey:venueIdString]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:j inSection:i];
[indexArray addObject:indexPath];
}
j++;
}
}
i++;
}
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade];
}
However I notice that the cellForRowAtIndexPath function is never called after and the cells do not get updated correctly. Any idea what the issue might be?
EDIT: I just noticed that if I scroll out of the view of the cell that is supposed to get updated then it gets updated when I scroll back into view. Is there no way to have it update while in view?
How about wrap it?
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];
Here are some similar problems I have found.
cellForRowAtIndexPath isn't called after reloadRowsAtIndexPaths
UITableView's reloadRowsAtIndexPaths: (NSArray *) indexPaths failing to cause a reload unless you call it twice?
Hope this helps.
EDIT : I think you are not getting indexPath check section might be constant as you are increasing while each object gets traversed:
- (void)reloadPaths:(NSMutableDictionary *)bookingsDict
{
NSMutableArray* indexArray = [NSMutableArray array];
int i = 0;
for (NSString *dateKey in self.eventsToShow) {
NSMutableDictionary *venues = [bookingsDict objectForKey:dateKey];
if (venues) {
int j = 0;
NSArray *events = [self.eventsToShow objectForKey:dateKey];
for (DCTEvent *event in events) {
NSString *venueIdString = [NSString stringWithFormat:#"%#", event.eventVenueId];
if ([venues objectForKey:venueIdString]) {
//changed here as section might be constant as i think it might be
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:j inSection:0];
[indexArray addObject:indexPath];
}
j++;
}
}
i++;
}
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
Try this :
//your code here
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
For me pushing my reload code to the main queue solved the problem
DispatchQueue.main.async {
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}

TableView updates not happening

I am inserting and removing rows at random from a tableview. However, if the rows I want to remove are not in view, they are not actually removed in the UI when the given row does come back to view.
If I add and remove the rows only on visible rows, it works just fine. What could be causing this problem?
- (void)sectionHeaderView:(BaseSectionHeaderView *)sectionHeaderView sectionOpened:(NSInteger)sectionOpened {
LogInfo(#"opening section:%ld",sectionOpened);
[_dataSource setSectionAtIndex:sectionOpened Open:YES];
/*
Create an array containing the index paths of the rows to insert: These correspond to the rows for each quotation in the current section.
*/
NSInteger countOfRowsToInsert = [_dataSource tableView:_tableView numberOfRowsInSection:sectionOpened];
NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
[indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:sectionOpened]];
}
/*
Create an array containing the index paths of the rows to delete: These correspond to the rows for each quotation in the previously-open section, if there was one.
*/
NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
if (self.openSection.openSectionHeaderView != nil) {
[_openSection.openSectionHeaderView toggleOpenWithUserAction:NO];
// NSInteger countOfRowsToDelete = [_purchaseInvoiceListTable.tableView numberOfRowsInSection:self.openSection.openSectionIndex];
NSInteger countOfRowsToDelete = [_dataSource tableView:_tableView numberOfRowsInSection:self.openSection.openSectionIndex];
[_dataSource setSectionAtIndex:self.openSection.openSectionIndex Open:NO];
for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
[indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:self.openSection.openSectionIndex]];
}
}
// style the animation so that there's a smooth flow in either direction
UITableViewRowAnimation insertAnimation;
UITableViewRowAnimation deleteAnimation;
if (self.openSection.openSectionHeaderView == nil || sectionOpened < self.openSection.openSectionIndex) {
insertAnimation = UITableViewRowAnimationTop;
deleteAnimation = UITableViewRowAnimationBottom;
}
else {
insertAnimation = UITableViewRowAnimationBottom;
deleteAnimation = UITableViewRowAnimationTop;
}
// apply the updates
[_tableView beginUpdates];
[_tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:insertAnimation];
[_tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:deleteAnimation];
[_tableView endUpdates];
self.openSection.openSectionIndex = sectionOpened;
self.openSection.openSectionHeaderView = sectionHeaderView;
self.openSection.sectionHeight = [NSNumber numberWithFloat:sectionHeaderView.frame.size.height];
LogInfo(#"sectionOpened:%ld",sectionOpened);
}
i think you should reload the table view by this...
[tableView reloadData];
and if from this it will not give the expected result then remove the object from array that fill the table view's row. i think this will definitely work...
tell me is it working or not...

Expandable tableview cells

My current tableview expands the cells on click, example:
Parent 0
-Child 1
-Child 2
-Child 3
What I'm struggling to do is, when I expand a cell all the others will close, I'm trying to make sure only one cell Is open at the time. Can you guys give any ideas on how to do it?
Current code for expanding the cell:
- (void)tableView:(UITableView *)tableView1 didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section==0) {
if([d valueForKey:#"produtos"]) {
NSArray *ar=[d valueForKey:#"produtos"];
BOOL isAlreadyInserted=NO;
for(NSDictionary *dInner in ar ){
NSInteger index=[self.firstForTable indexOfObjectIdenticalTo:dInner];
isAlreadyInserted=(index>0 && index!=NSIntegerMax);
if(isAlreadyInserted) break;
}
if(isAlreadyInserted) {
[self miniMizeFirstsRows:ar];
} else {
NSUInteger count=indexPath.row+1;
NSMutableArray *arCells=[NSMutableArray array];
for(NSDictionary *dInner in ar ) {
[arCells addObject:[NSIndexPath indexPathForRow:count inSection:0]];
[self.firstForTable insertObject:dInner atIndex:count++];
}
[tableView1 beginUpdates];
[tableView1 insertRowsAtIndexPaths:arCells withRowAnimation:UITableViewRowAnimationNone];
[tableView1 endUpdates];
[tableView1 scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}else{
NSLog(#"child %# %#|",[item valueForKey:#"nome"],[item valueForKey:#"produtos"]);
}
}
Current code for minimizing the cell:
-(void)miniMizeFirstsRows:(NSArray*)ar{
NSLog(#"miniMizeFirstsRows");
for(NSDictionary *dInner in ar ) {
NSUInteger indexToRemove=[self.firstForTable indexOfObjectIdenticalTo:dInner];
NSArray *arInner=[dInner valueForKey:#"produtos"];
if(arInner && [arInner count]>0){
[self miniMizeFirstsRows:arInner];
}
if([self.firstForTable indexOfObjectIdenticalTo:dInner]!=NSNotFound) {
[self.firstForTable removeObjectIdenticalTo:dInner];
[tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:
[NSIndexPath indexPathForRow:indexToRemove inSection:0]
]
withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
}
}
Thanks in Advance.
EDIT still Can't make it work
What I have, by using the help from Marco answer:
NSLog(#"indexPath1 = %i",selectedRow);
NSDictionary *d=[self.firstForTable objectAtIndex:indexPath.row];
if([d valueForKey:#"Objects"]) {
NSArray *ar=[d valueForKey:#"Objects"];
NSUInteger count=indexPath.row +1;
NSMutableArray *arCells=[NSMutableArray array];
for(NSDictionary *dInner in ar ) {
[arCells addObject:[NSIndexPath indexPathForRow:count inSection:0]];
[self.firstForTable insertObject:dInner atIndex:count++];
}
[tableView insertRowsAtIndexPaths:arCells withRowAnimation:UITableViewRowAnimationLeft];
// }
}else
{
NSLog(#"Leave Element:::%# %#|",[d valueForKey:#"name"],[d valueForKey:#"book"]);
}
// The user is selecting the cell which is currently expanded
// we want to minimize it back
if (selectedRow == row)
{
NSLog(#"selectedRow2 = %i",selectedRow);
NSDictionary *d=[self.firstForTable objectAtIndex:selectedRow];
if([d valueForKey:#"Objects"]) {
NSArray *ar=[d valueForKey:#"Objects"];
[self miniMizeFirstsRows:ar];
}
selectedRow = -1;
return;
}
// First we check if a cell is already expanded.
// If it is we want to minimize make sure it is reloaded to minimize it back
if (selectedRow >= 0)
{
NSLog(#"selectedRow3 = %i",selectedRow);
NSDictionary *d=[self.firstForTable objectAtIndex:selectedRow];
if([d valueForKey:#"Objects"]) {
NSArray *ar=[d valueForKey:#"Objects"];
[self miniMizeFirstsRows:ar];
}
selectedRow = row;
}
// Finally set the selected index to the new selection and reload it to expan
selectedRow = row;
[tableView beginUpdates]; [tableView endUpdates];
}
Some more help please :)
One thing missing from Marco's answer is the implementation of "heightForRowAtIndexPath" Delegate method.
So these are the steps you need to follow:
Create a variable to record the selected row number
Update that variable when a row is selected (in "didSelectRowAtIndexPath" delegate method) and call tableView's beginUpdates & endUpdates methods.
Return bigger height when indexPath.row == selectedRow, else return normal height.
You need an instance variable to track your selected cell.
NOTE: The code below is not meant to replace your tableview data source and delegate methods, but as an example to track a selected cell:
Add an NSInteger selectedRow instance variable to your view controller.
In viewDidLoad, initialize selectedRow to indicate no cell will be expanded:
- (void)viewDidLoad
{
[super viewDidLoad];
// Set set our selectedRow to -1 to indicate no cell will be expanded
selectedRow = -1;
}
In tableView:didSelectRowAtIndexPath::
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger row = indexPath.row;
// The user is selecting the cell which is currently expanded
// we want to minimize it back
if (selectedRow == row)
{
selectedRow = -1;
[tableView beginUpdates]; [tableView endUpdates];
return;
}
// First we check if a cell is already expanded.
// If it is we want to minimize make sure it is reloaded to minimize it back
if (selectedRow >= 0)
{
selectedRow = row;
}
// Finally set the selected index to the new selection and reload it to expand
selectedRow = row;
[tableView beginUpdates]; [tableView endUpdates];
}

Invalid update: invalid number of rows in section 0 UITableView

I know this has been asked before 50 times but I have looked through all the other answers and didn't find anything that could fix my issue.
Anyway this is my code:
NSMutableArray *cellIndicesToBeDeleted = [[NSMutableArray alloc] init];
for (int i = 0; i < [thetableView numberOfRowsInSection:0]; i++) {
NSIndexPath *p = [NSIndexPath indexPathForRow:i inSection:0];
if ([[thetableView cellForRowAtIndexPath:p] accessoryType] == UITableViewCellAccessoryCheckmark) {
[cellIndicesToBeDeleted addObject:p];
[self.cellArray removeObjectAtIndex:i];
}
}
[thetableView deleteRowsAtIndexPaths:cellIndicesToBeDeleted withRowAnimation:UITableViewRowAnimationLeft];
[cellIndicesToBeDeleted release];
And when I execute this code I get this crash log:
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Does anyone see what I am doing wrong?
Newish code:
NSMutableIndexSet *mutableIndexSet = [[NSMutableIndexSet alloc] init];
NSMutableArray *cellIndicesToBeDeleted = [[NSMutableArray alloc] init];
for (int i = 0; i < [thetableView numberOfRowsInSection:0]; i++) {
NSIndexPath *p = [NSIndexPath indexPathForRow:i inSection:0];
if ([[thetableView cellForRowAtIndexPath:p] accessoryType] == UITableViewCellAccessoryCheckmark) {
[cellIndicesToBeDeleted addObject:p];
[mutableIndexSet addIndex:p.row];
}
}
[self.cellArray removeObjectsAtIndexes:mutableIndexSet];
[thetableView deleteRowsAtIndexPaths:cellIndicesToBeDeleted withRowAnimation:UITableViewRowAnimationLeft];
[cellIndicesToBeDeleted release];
[mutableIndexSet release];
I figured it out it had to do with the numberOfRowsInSection. I wasn't updating my nsuserdefaults with the updated datasource so I did that now and it works perfectly. Here is my updated code by the way:
//Code to delete rows
NSMutableIndexSet *mutableIndexSet = [[NSMutableIndexSet alloc] init];
NSMutableArray *cellIndicesToBeDeleted = [[NSMutableArray alloc] init];
for (int i = [thetableView numberOfRowsInSection:0] - 1; i >= 0; i--) {
NSIndexPath *p = [NSIndexPath indexPathForRow:i inSection:0];
if ([[thetableView cellForRowAtIndexPath:p] accessoryType] == UITableViewCellAccessoryCheckmark) {
[cellIndicesToBeDeleted addObject:p];
[mutableIndexSet addIndex:p.row];
}
}
[self.cellArray removeObjectsAtIndexes:mutableIndexSet];
[[NSUserDefaults standardUserDefaults] setObject:self.cellArray forKey:#"cellArray"];
[thetableView deleteRowsAtIndexPaths:cellIndicesToBeDeleted withRowAnimation:UITableViewRowAnimationLeft];
[cellIndicesToBeDeleted release];
[mutableIndexSet release];
You need to add the following before and after you remove the row:
[theTableView beginUpdates];
// your code goes here
[theTableView endUpdates];

Resources