请选择 进入手机版 | 继续访问电脑版

技术控

    今日:39| 主题:54677
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] 如何实现可收起和展开的Table Section

[复制链接]
陌尘ㄨ 发表于 2016-10-2 15:16:27
105 1

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

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

  这是一个简单的iOS swift项目,旨在介绍如何实现可收起和展开的table section,并且,项目不需要main storyboard, XIB, 注册nib等,只需要纯的Swfit代码!
   如何实现可收起和展开的Table Section-1-技术控-iPhone,源代码,master,false,如何
   项目源代码: https://github.com/jeantimex/ios-swift-collapsible-table-section
   如果你希望获得Swift 3.0的代码,可以在 migrate-to-swift-3.0 分支里找到, 最终将会汇入master分支。
  效果

   

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

如何实现可收起和展开的Table Section-2-技术控-iPhone,源代码,master,false,如何

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

  第一步. 准备数据

   假设我们有如下的数据,它已经按照不同的section进行组织和整理,每个section都是一个 Section 结构(或对象):
  1. struct Section {
  2.   var name: String!
  3.   var items: [String]!
  4.   var collapsed: Bool!
  5.   init(name: String, items: [String], collapsed: Bool = false) {
  6.     self.name = name
  7.     self.items = items
  8.     self.collapsed = collapsed
  9.   }
  10. }
  11. var sections = [Section]()
  12. sections = [
  13.   Section(name: "Mac", items: ["MacBook", "MacBook Air", "MacBook Pro", "iMac", "Mac Pro", "Mac mini", "Accessories", "OS X El Capitan"]),
  14.   Section(name: "iPad", items: ["iPad Pro", "iPad Air 2", "iPad mini 4", "Accessories"]),
  15.   Section(name: "iPhone", items: ["iPhone 6s", "iPhone 6", "iPhone SE", "Accessories"])
  16. ]
复制代码
  collapsed 表示当前的section是否被收起或展开,默认下是 false ,即展开。
  第二步. Section Header

   根据 苹果 API reference , 我们应该使用 UITableViewHeaderFooterView . 让我们创建一个section header的类来继承它,我们把这个section header类起名为 CollapsibleTableViewHeader :
  1. class CollapsibleTableViewHeader: UITableViewHeaderFooterView {
  2.     let titleLabel = UILabel()
  3.     let arrowLabel = UILabel()
  4.     override init(reuseIdentifier: String?) {
  5.         super.init(reuseIdentifier: reuseIdentifier)
  6.         contentView.addSubview(titleLabel)
  7.         contentView.addSubview(arrowLabel)
  8.     }
  9.     required init?(coder aDecoder: NSCoder) {
  10.         fatalError("init(coder:) has not been implemented")
  11.     }
  12. }
复制代码
  当用户点击section header的时候我们需要收起或者展开这个section,为了实现这样的效果,让我们借用一下 UITapGestureRecognizer . 同时我们需要将这个tap事件通知给table view并让它来更新section的 collapsed 值。
  1. protocol CollapsibleTableViewHeaderDelegate {
  2.     func toggleSection(header: CollapsibleTableViewHeader, section: Int)
  3. }
  4. class CollapsibleTableViewHeader: UITableViewHeaderFooterView {
  5.     var delegate: CollapsibleTableViewHeaderDelegate?
  6.     var section: Int = 0
  7.     ...
  8.     override init(reuseIdentifier: String?) {
  9.         super.init(reuseIdentifier: reuseIdentifier)
  10.         ...
  11.         addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(CollapsibleTableViewHeader.tapHeader(_:))))
  12.     }
  13.     ...
  14.     func tapHeader(gestureRecognizer: UITapGestureRecognizer) {
  15.         guard let cell = gestureRecognizer.view as? CollapsibleTableViewHeader else {
  16.             return
  17.         }
  18.         delegate?.toggleSection(self, section: cell.section)
  19.     }
  20.     func setCollapsed(collapsed: Bool) {
  21.         // Animate the arrow rotation (see Extensions.swf)
  22.         arrowLabel.rotate(collapsed ? 0.0 : CGFloat(M_PI_2))
  23.     }
  24. }
复制代码
  既然我们不用任何storyboard或者XIB,如何实现自动布局呢?答案是运用 NSLayoutConstraint 的 constraintsWithVisualFormat 函数。
  1. override init(reuseIdentifier: String?) {
  2.     ...
  3.     // arrowLabel must have fixed width and height
  4.     arrowLabel.widthAnchor.constraintEqualToConstant(12).active = true
  5.     arrowLabel.heightAnchor.constraintEqualToConstant(12).active = true
  6.     titleLabel.translatesAutoresizingMaskIntoConstraints = false
  7.     arrowLabel.translatesAutoresizingMaskIntoConstraints = false
  8. }
  9. override func layoutSubviews() {
  10.     super.layoutSubviews()
  11.     ...
  12.     let views = [
  13.         "titleLabel" : titleLabel,
  14.         "arrowLabel" : arrowLabel,
  15.     ]
  16.     contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
  17.         "H:|-20-[titleLabel]-[arrowLabel]-20-|",
  18.         options: [],
  19.         metrics: nil,
  20.         views: views
  21.     ))
  22.     contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
  23.         "V:|-[titleLabel]-|",
  24.         options: [],
  25.         metrics: nil,
  26.         views: views
  27.     ))
  28.     contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
  29.         "V:|-[arrowLabel]-|",
  30.         options: [],
  31.         metrics: nil,
  32.         views: views
  33.     ))
  34. }
复制代码
第三步. UITableView DataSource 以及 Delegate

   首先,sections的数量为 sections.count :
  1. override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
  2.   return sections.count
  3. }
复制代码
每个section里面cell的数量为:
  1. override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  2.     return sections[section].items.count
  3. }
复制代码
  接下来使用tableView的 viewForHeaderInSection 函数来渲染我们的section header:
  1. override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
  2.     let header = tableView.dequeueReusableHeaderFooterViewWithIdentifier("header") as? CollapsibleTableViewHeader ?? CollapsibleTableViewHeader(reuseIdentifier: "header")
  3.     header.titleLabel.text = sections[section].name
  4.     header.arrowLabel.text = ">"
  5.     header.setCollapsed(sections[section].collapsed)
  6.     header.section = section
  7.     header.delegate = self
  8.     return header
  9. }
复制代码
普通的cell就很简单了,没什么好说的:
  1. override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  2.     let cell = tableView.dequeueReusableCellWithIdentifier("cell") as UITableViewCell? ?? UITableViewCell(style: .Default, reuseIdentifier: "cell")
  3.     cell.textLabel?.text = sections[indexPath.section].items[indexPath.row]
  4.     return cell
  5. }
复制代码
最后一步. 如何收起和展开?

   思路超级简单!如果该section的 collapsed 值为 true , 我们就将这个section里所有cell的高度都设为 0 , 否则为 44.0 !
  1. override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
  2.     return sections[indexPath.section].collapsed! ? 0 : 44.0
  3. }
复制代码
切换收起和展开的函数如下:
  1. extension CollapsibleTableViewController: CollapsibleTableViewHeaderDelegate {
  2.     func toggleSection(header: CollapsibleTableViewHeader, section: Int) {
  3.         let collapsed = !sections[section].collapsed
  4.         // Toggle collapse
  5.         sections[section].collapsed = collapsed
  6.         header.setCollapsed(collapsed)
  7.         // Adjust the height of the rows inside the section
  8.         tableView.beginUpdates()
  9.         for i in 0 ..< sections[section].items.count {
  10.             tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: i, inSection: section)], withRowAnimation: .Automatic)
  11.         }
  12.         tableView.endUpdates()
  13.     }
  14. }
复制代码
注意到我们不是简单的重绘整个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

如何实现可收起和展开的Table Section-3-技术控-iPhone,源代码,master,false,如何

  作者: Yong Su @ Box Inc.



上一篇:PyCon China 深圳站精彩回顾(附PPT及视频)
下一篇:Markcook2.0,使用 Vue2.0 和 Vuex2.0 进行完全重构升级
山怀 发表于 2016-10-19 08:21:51
very good
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读


回页顶回复上一篇下一篇回列表
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )

© 2001-2017 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表