返回 导航

Swift

hangge.com

Swift - 实现点击UITableView单元格时自动展开单元格

作者:hangge | 2015-06-05 09:38
(本文代码已升级至Swift4)

下面是一个列表单元格cell的折叠展开效果的demo。
当点击单元格时会展开该单元格,便于显示一些详情什么的。点击其他单元格原来的会关闭,同时有动画效果。

效果如如下:
 
代码如下:
import UIKit

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
    
    var tableView:UITableView?
    
    var ctrlnames:[String] = ["UILabel 标签","UIButton 按钮","UIDatePiker 日期选择器",
                              "UITableView 表格视图"]
    
    var selectedCellIndexPath:IndexPath!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //创建表视图
        self.tableView = UITableView(frame: UIScreen.main.bounds,
                                     style:UITableViewStyle.plain)
        self.tableView!.delegate = self
        self.tableView!.dataSource = self
        //创建一个重用的单元格
        self.tableView!.register(UITableViewCell.self,
                                      forCellReuseIdentifier: "SwiftCell")
        self.view.addSubview(self.tableView!)
    }
    
    //在本例中,只有一个分区
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    //返回表格行数(也就是返回控件数)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.ctrlnames.count
    }
    
    //创建各单元显示内容(创建参数indexPath指定的单元)
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
        -> UITableViewCell {
        let label =  UILabel(frame: CGRect.zero)
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = self.ctrlnames[indexPath.row]
        
        let textview=UITextView(frame: CGRect.zero)
        textview.translatesAutoresizingMaskIntoConstraints = false
        textview.textColor = UIColor.gray
        //演示效果,暂时写死
        textview.text = "UIDatePicker是一个控制器类,封装了UIPickerView,但是他是UIControl的子类"
        
        let identify:String = "SwiftCell"
        let cell = UITableViewCell(style: .default, reuseIdentifier:identify)
        //自动遮罩不可见区域,超出的不显示
        cell.layer.masksToBounds = true
        cell.contentView.addSubview(label)
        cell.contentView.addSubview(textview)
        
        //创建一个控件数组
        let views = ["label":label, "textview":textview]
        cell.contentView.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "H:|-15-[label]-15-|", options: [], metrics: nil,
            views: views))
        cell.contentView.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "H:|-15-[textview]-15-|", options: [], metrics: nil,
            views: views))
        cell.contentView.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:|[label(40)]", options: [], metrics: nil,
            views: views))
        cell.contentView.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:|-40-[textview(80)]", options: [], metrics: nil,
            views: views))
        
        return cell
    }
    
    // UITableViewDelegate 方法,处理列表项的选中事件
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        self.tableView!.deselectRow(at: indexPath, animated: false)
        selectedCellIndexPath = indexPath
        // Forces the table view to call heightForRowAtIndexPath
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }
    
    //点击单元格会引起cell高度的变化,所以要重新设置
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath)
        -> CGFloat {
        if(selectedCellIndexPath != nil && selectedCellIndexPath == indexPath){
            return 120
        }
        return 40
    }
}
关于 tableView.reloadRows(at: [indexPath], with: .automatic) 的说明:
1,上面方法只会重新刷新 indexPath 这个单元格内容(即调用其 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)方法)。在实际项目中,我们就可以在单元格打开的时候再加载详细内容,节约资源。
2,上面方法调用后会重新设置所有的单元格高度(即调用 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath)),就算传个空数组进去也一样(tableView.reloadRows(at: [], with: .automatic)


功能修改:单元格展开收缩单独控制,不影响其他单元格
上面样例是每次只能打开一个单元格,新的单元格展开后、原来展开的单元格就会自动关闭。我们可以稍作修改,让每个单元格的展开收缩互不影响。

实现原理就是原来只记录最近一次选中单元格的 IndexPath,现在我们使用数组记录所有展开的单元格的 IndexPath(下面高亮处表示修改的地方)
import UIKit

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
    
    var tableView:UITableView?
    
    var ctrlnames:[String] = ["UILabel 标签","UIButton 按钮","UIDatePiker 日期选择器",
                              "UITableView 表格视图"]
    
    var selectedCellIndexPaths:[IndexPath] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //创建表视图
        self.tableView = UITableView(frame: UIScreen.main.bounds,
                                     style:UITableViewStyle.plain)
        self.tableView!.delegate = self
        self.tableView!.dataSource = self
        //创建一个重用的单元格
        self.tableView!.register(UITableViewCell.self,
                                      forCellReuseIdentifier: "SwiftCell")
        self.view.addSubview(self.tableView!)
    }
    
    //在本例中,只有一个分区
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    //返回表格行数(也就是返回控件数)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.ctrlnames.count
    }
    
    //创建各单元显示内容(创建参数indexPath指定的单元)
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
        -> UITableViewCell {
        let label =  UILabel(frame: CGRect.zero)
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = self.ctrlnames[indexPath.row]
        
        let textview=UITextView(frame: CGRect.zero)
        textview.translatesAutoresizingMaskIntoConstraints = false
        textview.textColor = UIColor.gray
        //演示效果,暂时写死
        textview.text = "UIDatePicker是一个控制器类,封装了UIPickerView,但是他是UIControl的子类"
        
        let identify:String = "SwiftCell"
        let cell = UITableViewCell(style: .default, reuseIdentifier:identify)
        //自动遮罩不可见区域,超出的不显示
        cell.layer.masksToBounds = true
        cell.contentView.addSubview(label)
        cell.contentView.addSubview(textview)
        
        //创建一个控件数组
        let views = ["label":label, "textview":textview]
        cell.contentView.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "H:|-15-[label]-15-|", options: [], metrics: nil,
            views: views))
        cell.contentView.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "H:|-15-[textview]-15-|", options: [], metrics: nil,
            views: views))
        cell.contentView.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:|[label(40)]", options: [], metrics: nil,
            views: views))
        cell.contentView.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:|-40-[textview(80)]", options: [], metrics: nil,
            views: views))
        
        return cell
    }
    
    // UITableViewDelegate 方法,处理列表项的选中事件
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        self.tableView!.deselectRow(at: indexPath, animated: false)
        if let index = selectedCellIndexPaths.index(of: indexPath) {
            selectedCellIndexPaths.remove(at: index)
        }else{
            selectedCellIndexPaths.append(indexPath)
        }
        //强制改变高度
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }
    
    //点击单元格会引起cell高度的变化,所以要重新设置
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath)
        -> CGFloat {
        if selectedCellIndexPaths.contains(indexPath) {
            return 120
        }
        return 40
    }
}
评论

全部评论(10)

回到顶部