如何实现可收起和展开的Table Section

如何实现可收起和展开的Table Section 这是一个简单的iOS swift项目,旨在介绍如何实现可收起和展开的table section,并且,项目不需要

如何实现可收起和展开的Table Section

这是一个简单的iOS swift项目,旨在介绍如何实现可收起和展开的table section,并且,项目不需要main storyboard, XIB, 注册nib等,只需要纯的Swfit代码!

如何实现可收起和展开的Table Section

项目源代码: https://github.com/jeantimex/ios-swift-collapsible-table-section

如果你希望获得Swift 3.0的代码,可以在 migrate-to-swift-3.0 分支里找到, 最终将会汇入master分支。

效果

如何实现可收起和展开的Table Section

如何实现可收起和展开的Table Section?

第一步. 准备数据

假设我们有如下的数据,它已经按照不同的section进行组织和整理,每个section都是一个 Section 结构(或对象):

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 表示当前的section是否被收起或展开,默认下是 false ,即展开。

第二步. Section Header

根据 苹果 API reference , 我们应该使用 UITableViewHeaderFooterView . 让我们创建一个section header的类来继承它,我们把这个section header类起名为 CollapsibleTableViewHeader :

class CollapsibleTableViewHeader: UITableViewHeaderFooterView {

let titleLabel = UILabel()

let arrowLabel = UILabel()

override init(reuseIdentifier: String?) {

super.init(reuseIdentifier: reuseIdentifier)

contentView.addSubview(titleLabel)

contentView.addSubview(arrowLabel)

}

required init?(coder aDecoder: NSCoder) {

fatalError("init(coder:) has not been implemented")

}

}

当用户点击section header的时候我们需要收起或者展开这个section,为了实现这样的效果,让我们借用一下 UITapGestureRecognizer . 同时我们需要将这个tap事件通知给table view并让它来更新section的 collapsed 值。

protocol CollapsibleTableViewHeaderDelegate {

func toggleSection(header: CollapsibleTableViewHeader, section: Int)

}

class CollapsibleTableViewHeader: UITableViewHeaderFooterView {

var delegate: CollapsibleTableViewHeaderDelegate?

var section: Int = 0

...

override init(reuseIdentifier: String?) {

super.init(reuseIdentifier: reuseIdentifier)

...

addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(CollapsibleTableViewHeader.tapHeader(_:))))

}

...

func tapHeader(gestureRecognizer: UITapGestureRecognizer) {

guard let cell = gestureRecognizer.view as? CollapsibleTableViewHeader else {

return

}

delegate?.toggleSection(self, section: cell.section)

}

func setCollapsed(collapsed: Bool) {

// Animate the arrow rotation (see Extensions.swf)

arrowLabel.rotate(collapsed ? 0.0 : CGFloat(M_PI_2))

}

}

既然我们不用任何storyboard或者XIB,如何实现自动布局呢?答案是运用 NSLayoutConstraintconstraintsWithVisualFormat 函数。

override init(reuseIdentifier: String?) {

...

// arrowLabel must have fixed width and height

arrowLabel.widthAnchor.constraintEqualToConstant(12).active = true

arrowLabel.heightAnchor.constraintEqualToConstant(12).active = true

titleLabel.translatesAutoresizingMaskIntoConstraints = false

arrowLabel.translatesAutoresizingMaskIntoConstraints = false

}

override func layoutSubviews() {

super.layoutSubviews()

...

let views = [

"titleLabel" : titleLabel,

"arrowLabel" : arrowLabel,

]

contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(

"H:|-20-[titleLabel]-[arrowLabel]-20-|",

options: [],

metrics: nil,

views: views

))

contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(

"V:|-[titleLabel]-|",

options: [],

metrics: nil,

views: views

))

contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(

"V:|-[arrowLabel]-|",

options: [],

metrics: nil,

views: views

))

}

第三步. UITableView DataSource 以及 Delegate

首先,sections的数量为 sections.count :

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {

return sections.count

}

每个section里面cell的数量为:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return sections[section].items.count

}

接下来使用tableView的 viewForHeaderInSection 函数来渲染我们的section header:

override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

let header = tableView.dequeueReusableHeaderFooterViewWithIdentifier("header") as? CollapsibleTableViewHeader ?? CollapsibleTableViewHeader(reuseIdentifier: "header")

header.titleLabel.text = sections[section].name

header.arrowLabel.text = ">"

header.setCollapsed(sections[section].collapsed)

header.section = section

header.delegate = self

return header

}

普通的cell就很简单了,没什么好说的:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell? ?? UITableViewCell(style: .Default, reuseIdentifier: "cell")

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

return cell

}

最后一步. 如何收起和展开?

思路超级简单!如果该section的 collapsed 值为 true , 我们就将这个section里所有cell的高度都设为 0 , 否则为 44.0 !

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

return sections[indexPath.section].collapsed! ? 0 : 44.0

}

切换收起和展开的函数如下:

extension CollapsibleTableViewController: CollapsibleTableViewHeaderDelegate {

func toggleSection(header: CollapsibleTableViewHeader, section: Int) {

let collapsed = !sections[section].collapsed

// Toggle collapse

sections[section].collapsed = collapsed

header.setCollapsed(collapsed)

// Adjust the height of the rows inside the section

tableView.beginUpdates()

for i in 0 ..< sections[section].items.count {

tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: i, inSection: section)], withRowAnimation: .Automatic)

}

tableView.endUpdates()

}

}

注意到我们不是简单的重绘整个section,实际上我们只需要重绘section里的所有cell就好, 这样做的好处是避免了section header因重绘时闪烁的效果, 最重要是的可以让我们更平滑地处理我们想要的动画效果, 例如旋转那个箭头,改变背景颜色等等。

好了就这么多吧,如果你很感兴趣,请参考源码。

更多的关于table section收起和展开的项目

有时候你可能想要在grouped-style的table里实现section的收起和展开, 我写了另外一个demo, https://github.com/jeantimex/ios-swift-collapsible-table-section-in-grouped-section . 实现的方法其实很类似。

如何实现可收起和展开的Table Section

作者: Yong Su @ Box Inc.

未登录用户
全部评论0
到底啦