UITableView实现Accordion(手风琴)效果

Accordion(手风琴)效果在网页中十分常见。在屏幕更小,可显示范围更有限的移动端,更是使用频繁。

在 Web 开发中,使用 Javascript 操控 DOM,就可以轻松实现这种效果。

在 iOS 端呢,实现一个简易的 Accordion 效果,就简单程度而言,更是有过之而无不及。简直是一马平川、易如反掌,像过清晨的马路一样轻松。

OK,让我来演示一下。

Demo地址:https://github.com/4074/UITableViewAccordionDemo

storyboard操作

我们现在 storyboard 中创建一些必要的 view 和节点

  • 在 storyboard 新建一个 view controller;新建一个 table view,使用 auto layout 约束尺寸为 view 的大小;效果如图
屏幕快照 2016-07-02 下午8.13.47.png
  • 在 table view 中添加 cell;在 cell 添加 容器view,一个标题view,一个标题label;效果如图
屏幕快照 2016-07-02 下午8.18.02.png
  • 创建 UITableViewCell 的子类 AccordionTableViewCell
屏幕快照 2016-07-02 下午8.20.49.png
  • 设置 cell 的类 AccordionTableViewCell,identity为 Cell (方便后面使用 dequeueReusableCellWithIdentifier 获取 cell),然后把 cell 中的各个 view 用 IBOutlet 方式引入。
屏幕快照 2016-07-02 下午8.21.13.png
屏幕快照 2016-07-02 下午8.37.16.png
屏幕快照 2016-07-02 下午8.22.01.png

编写代码

接下来主要就是编写代码的时间了。当然代码量十分有限。

  • 我们来添加一下 cell 的样式,让它不至于太难看。主要是加一下背景、圆角。
class AccordionTableViewCell: UITableViewCell {

    @IBOutlet weak var wrapView: UIView!
    @IBOutlet weak var titleView: UIView!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        self.selectionStyle = .None
        
        wrapView.backgroundColor = UIColor.groupTableViewBackgroundColor()
        wrapView.layer.cornerRadius = 4
        wrapView.layer.masksToBounds = true
        
        titleView.backgroundColor = UIColor.groupTableViewBackgroundColor()
        titleView.layer.cornerRadius = 4
        titleView.layer.masksToBounds = true
    }
}
  • 把 table view 引入到 view controller 中,对其也进行一些设置,并把它的数据源、代理都指向 view controller。
tableView.separatorStyle = .None
tableView.dataSource = self
tableView.delegate = self
  • 定义一些必要的变量。
let cellHeight: CGFloat = 56  // 高度
let cellHeightExpanded: CGFloat = 360  // 展开高度

let cellCount = 24  // cell 数量
var cellStatus = [Bool](count: 24, repeatedValue: false)  // cell 的展开状态

let onlyOneExpanded = true  // 是否只允许一个展开
  • 最最重要的一步来了,扩展 view controller
extension ViewController: UITableViewDataSource, UITableViewDelegate {
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return cellCount
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! AccordionTableViewCell
        // 定义点击手势
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapTitleView(_:)))

        // 利用tag传递 cell 的行数
        cell.titleView.tag = indexPath.row

        // 绑定点击手势
        cell.titleView.addGestureRecognizer(tapGesture)

        return cell
    }
    
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        // 根据状态返回 cell 的高度
        return cellStatus[indexPath.row] ? cellHeightExpanded : cellHeight
    }
    
    // 点击标题的回调
    func tapTitleView(sender: UITapGestureRecognizer) {
        if let tag = sender.view?.tag {
            if cellStatus[tag] {
                cellStatus[tag] = false
            } else {
                // 如果只允许一个展开,则重置所有状态为 false
                if onlyOneExpanded {
                    cellStatus = [Bool](count: cellCount, repeatedValue: false)
                }
                cellStatus[tag] = true
            }
            
            // 更新 table view,则会更新高度
            tableView.beginUpdates()
            tableView.endUpdates()

            // 为了点击展开的 cell 不被遮挡,滚动到相应的 cell
            tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: tag, inSection: 0), atScrollPosition: .None, animated: true)
        }
    }
}

成果

这样,UITableView 简单的 Accordion 效果就完成了,让我们看一下最终的代码,和效果截图。

  • AccordionTableViewCell.swift
import UIKit

class AccordionTableViewCell: UITableViewCell {

    @IBOutlet weak var wrapView: UIView!
    @IBOutlet weak var titleView: UIView!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        self.selectionStyle = .None
        
        wrapView.backgroundColor = UIColor.groupTableViewBackgroundColor()
        wrapView.layer.cornerRadius = 4
        wrapView.layer.masksToBounds = true
        
        titleView.backgroundColor = UIColor.groupTableViewBackgroundColor()
        titleView.layer.cornerRadius = 4
        titleView.layer.masksToBounds = true
    }
}
  • ViewControler.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    
    let cellHeight: CGFloat = 56
    let cellHeightExpanded: CGFloat = 360
    
    let cellCount = 24
    var cellStatus = [Bool](count: 24, repeatedValue: false)
    
    let onlyOneExpanded = true
    
    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.separatorStyle = .None
        tableView.dataSource = self
        tableView.delegate = self
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return cellCount
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! AccordionTableViewCell
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapTitleView(_:)))
        cell.titleView.tag = indexPath.row
        cell.titleView.addGestureRecognizer(tapGesture)
        return cell
    }
    
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return cellStatus[indexPath.row] ? cellHeightExpanded : cellHeight
    }
    
    func tapTitleView(sender: UITapGestureRecognizer) {
        if let tag = sender.view?.tag {
            if cellStatus[tag] {
                cellStatus[tag] = false
            } else {
                if onlyOneExpanded {
                    cellStatus = [Bool](count: cellCount, repeatedValue: false)
                }
                cellStatus[tag] = true
            }
            
            tableView.beginUpdates()
            tableView.endUpdates()
            tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: tag, inSection: 0), atScrollPosition: .None, animated: true)
        }
    }
}
  • 效果截图。不知道怎么截动图,就放两个状态的截图吧
1.png

Demo地址:https://github.com/4074/UITableViewAccordionDemo

有什么疑问可以在留言中提出哦~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容