Reputation: 2405
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.
Upvotes: 3
Views: 20092
Reputation: 408
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
Upvotes: 1
Reputation: 581
- (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.
Upvotes: 0
Reputation: 2405
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
}
}
Upvotes: 10
Reputation: 921
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.
Upvotes: 2