扩展和折叠TableView的部分iOS?

4

我能够成功地展开和折叠tableView的各个部分,但是目前我还不能对单个部分进行操作。因此,所有部分都会同时折叠或展开,这是因为我调用了[tableView reloadData]。那么我该如何展开或折叠特定的部分呢?

以下是我目前的做法。

  -(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
  {
     headerLabel = [[UILabel alloc]init];
     headerLabel.tag = section;
     headerLabel.userInteractionEnabled = YES;
     headerLabel.backgroundColor = [[UIColor grayColor]colorWithAlphaComponent:0.2];
     headerLabel.text = [menuCategoryArray objectAtIndex:section];
     headerLabel.frame = CGRectMake(5, 0, tableView.tableHeaderView.frame.size.width, tableView.tableHeaderView.frame.size.height);


     UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(headerClicked:)];
     tapGesture.cancelsTouchesInView = NO;
     [headerLabel addGestureRecognizer:tapGesture];

     return headerLabel;


 }

 -(void)headerClicked:(UIGestureRecognizer*)sender
 {

    if (!isShowingList) {
    isShowingList=YES;
    [self.menuTableView reloadData];
    UILabel *lbl = (UILabel*)sender.view;
    NSLog(@"header no : %d", lbl.tag);

   }else{

    isShowingList=NO;
    [self.menuTableView reloadData];
    UILabel *lbl = (UILabel*)sender.view;
    NSLog(@"header no : %d", lbl.tag);

  }


  }

 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

if (isShowingList) {

    return [[[[jsonArray valueForKey:@"menus"] valueForKey:@"menuName"] objectAtIndex:section] count];

}else{
    return 0;
}
return 0;

}

1
为了展开使用 **insertRowsAtIndexPaths:withRowAnimation:**,而折叠使用 **deleteRowsAtIndexPaths:withRowAnimation:**。 - Pratyusha Terli
那么它应该在headerClicked方法中使用,对吧?如何获取插入indexPath的数组呢? - icodes
1
isShowingList 转换为数组(如果您有可变数量的部分,则可能是可变数组)。然后使用 section 作为数组的索引来确定每个部分是否应该展开。您可以通过测试手势识别器视图的标签来确定哪个部分标题被点击。 - pbasdf
非常感谢您的解释,并提供一些示例代码,这有助于我更好地理解 :-] - icodes
给我一个小时,我会给你提供我的代码。 - Pratyusha Terli
2个回答

5

你最好使用“tableView update block”来更新表格。请查看我最近发布的答案中的此viewController以获取答案。updateBlock允许您操作影响数据源的某些变量,指示表格添加/删除行/部分以反映该更改。请注意,当您调用endUpdates方法时,表格不得与模型冲突,否则会引发异常。

#import "ViewController.h"

//dont worry, the header is empty except for import <UIKit/UIKit.h>, this is a subclass on UIViewController

@interface ViewController ()<UITableViewDataSource, UITableViewDelegate>

@property (weak, nonatomic) UITableView *tableView;


@end

@implementation ViewController
{
//ivars
  BOOL sectionIsOpen[4]; //we will use this BOOL array to keep track of the open/closed state for each section.  Obviously I have the number of sections fixed at 4 here, but you could make a more dynamic array with malloc() if neccesary..
}



- (void)viewDidLoad {
  [super viewDidLoad];

  UITableView *tv = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
  tv.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  tv.dataSource = self;
  tv.delegate = self;

  [self.view addSubview:tv];
  self.tableView = tv;

  // Do any additional setup after loading the view, typically from a nib.
}

#pragma mark - UITableViewDataSource
-(NSInteger )numberOfSectionsInTableView:(UITableView *)tableView{
  return 4;
}

-(NSInteger )tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
  return ((sectionIsOpen[section]) ? [self numberOfRowsInSection:section] : 0);
}
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{

  //put your switch() here...

  return [NSString stringWithFormat:@"I am section %i", (int)section ];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

  static NSString *cellId = @"cellID";

  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];

  if (!cell) {
    cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId];
  }


 //etc etc decorate your cell...
  cell.textLabel.text = [NSString stringWithFormat:@"cell %i / %i", (int)indexPath.section, (int)indexPath.row ];


  return cell;
}
#pragma mark - UITableViewDelegate
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{

  const CGRect fr = CGRectMake(0, 0, 320.0, 40.0 );

  UIButton *btn = [[UIButton alloc]initWithFrame:fr];
  [btn setTitle:[self tableView:tableView titleForHeaderInSection:section] forState:UIControlStateNormal ];
  [btn setTag:section];
  [btn addTarget:self action:@selector(sectionOpenToggle:) forControlEvents:UIControlEventTouchUpInside];


  // add an image, colour etc if you like

  return btn;
}



#pragma mark - tableViewHelpers

//the number of rows in sectionX when it is open...
-(NSInteger )numberOfRowsInSection:(NSInteger )section{
//get your count from your model
  return section + 1;
}

//opening/closing a section
-(void )setSection:(NSInteger )section toOpen:(BOOL )open{

  if (open != sectionIsOpen[section]) {

//build an array of indexPath objects
    NSMutableArray *indxPths = [NSMutableArray array];
    for (NSInteger row = 0; row < [self numberOfRowsInSection:section]; row ++) {

      [indxPths addObject: [NSIndexPath indexPathForRow:row inSection:section ]
       ];

    }


    [self.tableView beginUpdates];

    if (open) {
      [self.tableView insertRowsAtIndexPaths:indxPths withRowAnimation:UITableViewRowAnimationFade];
      //nb there is a large ENUM of tableViewRowAnimation types to experiment with..

    }else{
      [self.tableView deleteRowsAtIndexPaths:indxPths withRowAnimation:UITableViewRowAnimationFade];

    }
    sectionIsOpen[section] = open;
    [self.tableView endUpdates];

  }
}

-(void )sectionOpenToggle:(id )sender{
  [self setSection:[sender tag] toOpen: !sectionIsOpen[[sender tag]] ];
}

// open/close all sections.
-(void )setAllSectionsOpen:(BOOL )open{

  for (NSInteger section = 0; section < [self numberOfSectionsInTableView:self.tableView]; section ++) {
    [self setSection:section toOpen:open];
  }
}


//these two for your convenience, hook up to navbar items etc..
-(IBAction)openAllSections:(id)sender{
  [self setAllSectionsOpen:YES];
}
-(IBAction)closeAllSections:(id)sender{
  [self setAllSectionsOpen:NO];
}
@end

5
首先将 isShowingList 视为
@property (nonatomic, strong) NSMutableArray *isShowingList;

而要识别先前打开的部分,您需要另一个属性。
@property (nonatomic, assign) NSInteger openSectionIndex;

当您初始化数据(在您的情况下是isShowingList)时,请在重新加载表格之前初始化isShowingList数组

self.isShowingList = [NSMutableArray array];
if (jsonArray && [jsonArray valueForKey:@"menus"] && [[jsonArray valueForKey:@"menus"] valueForKey:@"menuName"]) {
    for (int i = 0; i < [[[jsonArray valueForKey:@"menus"] valueForKey:@"menuName"] count]; i++) {
        [self.isShowingList addObject:[NSNumber numberWithBool:NO]];
    }
} 

并在viewDidLoad()中初始化openSectionIndex,像这样:

self.openSectionIndex = NSNotFound;

你的代码应该像这样进行更改

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if ([[self.isShowingList objectAtIndex:section] boolValue]) {
        return [[[[jsonArray valueForKey:@"menus"] valueForKey:@"menuName"] objectAtIndex:section] count];
    } else {
        return 0;
    }
    return 0;
}

-(void)headerClicked:(UIGestureRecognizer*)sender {
    UILabel *lbl = (UILabel*)sender.view;
    NSLog(@"header no : %d", lbl.tag);
    if ([[self.isShowingList objectAtIndex:lbl.tag] boolValue]) {
        [self closeSection:lbl.tag];
    } else {
        [self openSection:lbl.tag];
    }
}

//methods for expanding and collapsing sections
- (void)openSection:(NSInteger)section {
    [self.isShowingList replaceObjectAtIndex:section withObject:[NSNumber numberWithBool:YES]];
    NSInteger countOfRowsToInsert = [[[[jsonArray valueForKey:@"menus"] valueForKey:@"menuName"] objectAtIndex:section] count];
    NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
    for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
        [indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:section]];
    }
    NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
    NSInteger previousOpenSectionIndex = self.openSectionIndex;
    if (previousOpenSectionIndex != NSNotFound) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.menuTableView reloadSections:[NSIndexSet indexSetWithIndex:previousOpenSectionIndex] withRowAnimation:UITableViewRowAnimationNone];
        });
        [self.isShowingList replaceObjectAtIndex:previousOpenSectionIndex withObject:[NSNumber numberWithBool:NO]];
        NSInteger countOfRowsToDelete = [[[[jsonArray valueForKey:@"menus"] valueForKey:@"menuName"] objectAtIndex:previousOpenSectionIndex] count];
        for (NSInteger i = 0; i < countOfRowsToDelete; i++) {
            [indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:previousOpenSectionIndex]];
        }
    }
    // Apply the updates.
    [self.menuTableView beginUpdates];
    [self.menuTableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.menuTableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationAutomatic];
    [self.menuTableView endUpdates];
    self.openSectionIndex = section;
}

- (void)closeSection:(NSInteger)section {
    [self.isShowingList replaceObjectAtIndex:section withObject:[NSNumber numberWithBool:NO]];
    NSInteger countOfRowsToDelete = [self.menuTableView numberOfRowsInSection:section];
    if (countOfRowsToDelete > 0) {
        NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
        for (NSInteger i = 0; i < countOfRowsToInsert; i++) {
            [indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:section]];
        }
        [self.menuTableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationTop];
    }
    self.openSectionIndex = NSNotFound;
}

你的打开/关闭部分方法很脆弱,因为你在tableViewUpdates之外改变了self.isShowingList。在这些更新块之外,模型和表格之间的不一致是危险的。我建议你将beginUpdates调用剪切到修改此数组之前,参见我的示例(除此之外与你自己的相似)。我知道它在你那里运行没有错误,因为你在修改数组后立即调用beginUpdates,用户刚刚触摸并且不太可能滚动..但在我看来,这不是一个完美的设计。最好的问候 :) - Jef
@Pratyusha:谢谢。你的方法很完美,而且运行良好,只有一个问题。当我折叠一个部分时,该部分第一行的数据似乎会在标题上方浮动一小段时间,然后消失。 - icodes
在添加/删除单元格的方法中,尝试不同的动画效果,他已经使用了UITableViewRowAnimationTop和UITableViewRowAnimationAutomatic。 - Jef
@Jef:好的,谢谢。我会尝试的 :-) - icodes
1
感谢 @icodes 接受我的答案,正如 Jef 建议的那样,请尝试不同的动画,这可能会解决您的问题,并像他建议的那样将 beginUpdates 调用移动到修改数组之前。感谢您的建议 Jef,下次我会注意的。 - Pratyusha Terli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接