UITableView:嵌套的分区标题

7
我正在尝试实现一个带有以下结构的uitableview:
- 第0个section组 - 第0个section - cell 0 - cell 1 - cell 2 - 第1个section - cell 0 - cell 1 - cell 2 - 第1个section组 - 第0个section - cell 0 - cell 1 - cell 2 - 第1个section - cell 0 - cell 1 - cell 2
并且它应该像这个屏幕截图一样滚动(1-2-3-4): http://dl.dropbox.com/u/2213241/uitableview.png 所以始终显示两个sections。
我该如何实现这个?或者已经有人实现过了吗?
谢谢 :)

我的问题是,你想要实现类似那样的东西吗? - Robin
是的,我尝试过实现这个,但是我不知道该怎么做。所以我想有比我更聪明的人会有想法 ;) - hackfrag
这并不是很难实现,但也不简单,你可以通过子类化UITableView并发布一些相关问题来开始。 - Robin
3个回答

4
拥有嵌套的部分的技巧是在表视图中有两种行。一种用于表示第二级部分,另一种用于表示表视图中的常规行。假设您有一个两级数组(例如“sections”)来表示表视图中的项目。
那么,我们拥有的总部分数量就是顶级部分的数量。每个顶级部分中的行数将是子部分的数量加上每个子部分中的行数。
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.sections.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSArray *sectionItems = self.sections[(NSUInteger) section];
    NSUInteger numberOfRows = sectionItems.count; // For second level section headers
    for (NSArray *rowItems  in sectionItems) {
        numberOfRows += rowItems.count; // For actual table rows
    }
    return numberOfRows;
}

现在,我们需要考虑的是如何为表格视图创建行。在故事板中设置两个原型,分别用不同的重用标识符,一个用于节标题,另一个用于行项目,只需根据数据源方法中所请求的索引实例化正确的原型即可。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSMutableArray *sectionItems = self.sections[(NSUInteger) indexPath.section];
    NSMutableArray *sectionHeaders = self.sectionHeaders[(NSUInteger) indexPath.section];
    NSIndexPath *itemAndSubsectionIndex = [self computeItemAndSubsectionIndexForIndexPath:indexPath];
    NSUInteger subsectionIndex = (NSUInteger) itemAndSubsectionIndex.section;
    NSInteger itemIndex = itemAndSubsectionIndex.row;

    if (itemIndex < 0) {
        // Section header
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SECTION_HEADER_CELL" forIndexPath:indexPath];
        cell.textLabel.text = sectionHeaders[subsectionIndex];
        return cell;
    } else {
        // Row Item
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ROW_CONTENT_CELL" forIndexPath:indexPath];
        cell.textLabel.text = sectionItems[subsectionIndex][itemIndex];
        return cell;
    }
}

- (NSIndexPath *)computeItemAndSubsectionIndexForIndexPath:(NSIndexPath *)indexPath {
    NSMutableArray *sectionItems = self.sections[(NSUInteger) indexPath.section];
    NSInteger itemIndex = indexPath.row;
    NSUInteger subsectionIndex = 0;
    for (NSUInteger i = 0; i < sectionItems.count; ++i) {
        // First row for each section item is header
        --itemIndex;
        // Check if the item index is within this subsection's items
        NSArray *subsectionItems = sectionItems[i];
        if (itemIndex < (NSInteger) subsectionItems.count) {
            subsectionIndex = i;
            break;
        } else {
            itemIndex -= subsectionItems.count;
        }
    }
    return [NSIndexPath indexPathForRow:itemIndex inSection:subsectionIndex];
}

这里有一篇详细的文章,讲述如何实现这个功能。


我有类似的问题,但是更加深入,请帮忙解决。https://stackoverflow.com/q/45626816/6028575 - Jaydeep Vyas

3
如果有人对上述代码的Swift 4.2版本感兴趣,这里有一个。

为了拥有嵌套的部分,您需要在tableView中有几种类型的行。第一种表示第二级部分,第二种表示tableView中的标准行。假设您有一个两级数组(部分)来表示tableView中的项目。
然后,我们拥有的总部分数就是顶层部分的数量。每个顶层部分中的行数将是子部分的数量加上每个子部分中的行数。
func numberOfSections(in tableView: UITableView) -> Int {
    return sections.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let sectionItems = sections[section]
    var numberOfRows: Int = sectionItems.count // For second level section headers
    for rowItems: [Any] in sectionItems as? [[Any]] ?? [] {
        numberOfRows += rowItems.count // For actual table rows
    }
    return numberOfRows
}

现在,您只需要考虑如何创建tableView的行。在故事板中设置两个原型,分别用于不同的重用标识符,一个用于节标题,另一个用于行项目,然后根据数据源方法中的请求索引实例化正确的原型。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var sectionItems = sections[indexPath.section]
    var sectionHeaders = self.sectionHeaders[indexPath.section]
    let itemAndSubsectionIndex: IndexPath? = computeItemAndSubsectionIndex(for: indexPath)
    let subsectionIndex = Int(itemAndSubsectionIndex?.section ?? 0)
    let itemIndex: Int? = itemAndSubsectionIndex?.row

    if (itemIndex ?? 0) < 0 {
        // Section header
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "SECTION_HEADER_CELL", for: indexPath)
        cell.textLabel?.text = sectionHeaders[subsectionIndex] as? String
        return cell
    } else {
        // Row Item
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "ROW_CONTENT_CELL", for: indexPath)
        cell.textLabel?.text = sectionItems[subsectionIndex][itemIndex ?? 0] as? String
        return cell
    }
}

func computeItemAndSubsectionIndex(for indexPath: IndexPath?) -> IndexPath? {
    var sectionItems = sections[Int(indexPath?.section ?? 0)]
    var itemIndex: Int? = indexPath?.row
    var subsectionIndex: Int = 0
    for i in 0..<sectionItems.count {
        // First row for each section item is header
        itemIndex = (itemIndex ?? 0) - 1
        // Check if the item index is within this subsection's items
        let subsectionItems = sectionItems[i] as? [Any]
        if (itemIndex ?? 0) < Int(subsectionItems?.count ?? 0) {
            subsectionIndex = i
            break
        } else {
            itemIndex -= subsectionItems?.count
        }
    }
    return IndexPath(row: itemIndex ?? 0, section: subsectionIndex)
}

变量是如何声明的?你能举个例子吗? - Christian W

2
子类化 UITableView 是一个好的选择,就像 Robin 建议的那样。我自己也做过类似的事情,但是我子类化了 UITableViewCell 并将 UITableView 放在其中。在这种情况下,您将拥有一个基本的 tableView,其中每个部分都是一组。每行,因为您子类化了 UITableViewCell,则成为其自己的UITableView,它具有其自己的部分和行。这应该会给您所寻找的外观和功能。如果您在设置时遇到麻烦,我很乐意帮助您,但这并不难做到。这个教程 给出了一个很好的关于如何子类化 UITableViewCell 的例子,是一个很好的起点。

1
请将以下有关编程的内容从英语翻译成中文。仅返回已翻译的文本:类似于这样:http://iosstuff.wordpress.com/2011/06/29/adding-a-uitableview-inside-a-uitableviewcell/? - hackfrag
没错,那应该可以很好地工作。如果您不想使用IB,也可以通过编程方式完成。只需在头文件中设置tableView和其他变量,并在自定义单元格的initWithStyle:reuseIdentifier:方法中创建tableView即可。其余的代码将保持不变,但如果您不想为自定义单元格创建nib文件,那么这就是您的选择方式。 - justin
这是我认为非常有趣和创意的东西。谢谢分享。 - dreampowder

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