如何在IOS中实现可折叠/展开的Table Section

原文链接以及源代码:原文地址
原文作者:Yong Su @ Box Inc.

如何在IOS中实现可折叠/展开的Table Section?!

Demo效果

第一步:准备数据

假设我们有下面这些分好组的数据,每一组是一个 Section 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct Section {
var name: String!
var items: [String]!
var collapsed: Bool!

init(name: String, items: [String], collapsed: Bool = false) {
self.name = name
self.items = items
self.collapsed = collapsed
}
}

var sections = [Section]()

sections = [
Section(name: "Mac", items: ["MacBook", "MacBook Air", "MacBook Pro", "iMac", "Mac Pro", "Mac mini", "Accessories", "OS X El Capitan"]),
Section(name: "iPad", items: ["iPad Pro", "iPad Air 2", "iPad mini 4", "Accessories"]),
Section(name: "iPhone", items: ["iPhone 6s", "iPhone 6", "iPhone SE", "Accessories"])
]
collapsed 表明了现在是否是折叠状态,默认为false.

第二步:设计 Header 和 Cell

在story board中选择 Table View, 选择 Dynamic Prototypes并且设置 Prototype Cells 的值为 2, 一个给我们的自定义 Header,另外一个给我们每一行的 Cell, 然后将各自的 Identifier 设置为为 header 和 cell.

![](/img/IOSection/cell.png)

添加一个 UIButton (用做切换折叠状态) 和一个 Label 到 header 的 prototype cell 中, 创建一个继承自 UITableViewCell 的 swift 文件并且命名为CollapsibleTableViewHeader.swift. 这个文件很简单, 有两个 IBOutlets, 一个用做链接切换按钮 (UIButton), 另外一个用做链接 Label. 最后我们设置这个 header 的 Class 为 我们自定义的类 CollapsibleTableViewHeader 并且在类中正确的链接 IBOutlets.

现在这个文件应该看起来像这样:

1
2
3
4
5
6
7
8
import UIKit

class CollapsibleTableViewHeader: UITableViewCell {

@IBOutlet var titleLabel: UILabel!
@IBOutlet var toggleButton: UIButton!

}

创建一个 prototype cell 和一个继承自 UITableViewCell 的类, 有这两点好处:

  • 我们能可视化的设计我们的 header
  • 我们不需要像这样为了创建一个 Cell 而创建一个 nib 文件 然后在 tableView 中注册他使用:
1
2
let nib = UINib(nibName: "TableSectionHeader", bundle: nil)
tableView.registerNib(nib, forHeaderFooterViewReuseIdentifier: "TableSectionHeader")

个人而言, 我不喜欢在我的项目中有 nib 文件, 并且如果我们用了 dequeueReusableHeaderFooterViewWithIdentifier, 好像我们必须在那个 section 中最少拥有一行 row, 但这里我们需要 0 row!

第三步: UITableViewDelegate

首先,用 sections.count 正确的表示 section 的数量:

1
2
3
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}

对于那些每个 section 中的 row 的数量, 我们使用 collapsed 变量去控制它, 如果 collapsed 是 true, 我们就返回 0, 否则返回他真实 items 的数量:

1
2
3
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (sections[section].collapsed!) ? 0 : sections[section].items.count
}

我们使用 tableView 的 viewForHeaderInSection 方法去实现我们自定义的 header:

1
2
3
4
5
6
7
8
9
10
11
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableCellWithIdentifier("header") as! CollapsibleTableViewHeader

header.titleLabel.text = sections[section].name
header.toggleButton.tag = section
header.toggleButton.addTarget(self, action: #selector(CollapsibleTableViewController.toggleCollapse), forControlEvents: .TouchUpInside)

header.toggleButton.rotate(sections[section].collapsed! ? 0.0 : CGFloat(M_PI_2))

return header.contentView
}

注意我们为我们的接切换按钮 (UIButton) 注册了一个touch up inside 事件, 一旦这个按钮被按下, 它将会触发 toggleCollapse 方法.

最后, 我们的 row cell 看起来像这样:

1
2
3
4
5
6
7
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell!

cell.textLabel?.text = sections[indexPath.section].items[indexPath.row]

return cell
}

这个是 toggleCollapse 方法:

1
2
3
4
5
6
7
8
9
10
func toggleCollapse(sender: UIButton) {
let section = sender.tag
let collapsed = sections[section].collapsed

// Toggle collapse
sections[section].collapsed = !collapsed

// Reload section
tableView.reloadSections(NSIndexSet(index: section), withRowAnimation: .Automatic)
}

好啦,就是这么简单,如果你需要看源代码,请到顶部原文链接中,你可以在那个链接中找到具体的实现代码

更多的可折叠Demo

有时你可能要在grouped-style table中实现一些可折叠的cells, 我有一些单独的Demo在链接中. 他的实现和今天我们学的这个非常相似,只有一点点小不同,如果有兴趣可以去看下.

排序效果