当前位置: > > > Swift - 使用xib制作UITableView的自定义Cell(自定义单元格的复用)

Swift - 使用xib制作UITableView的自定义Cell(自定义单元格的复用)

在StoryBoard中,我们可以很方便地设置表格(tableView)内部单元格(cell)样式。但如果多个页面的tableView单元格样式都一样的话,再一个个单独设置不仅麻烦,而且会造成代码冗余。
最好的办法就是把单元格提取出来做成自定义组件,从而实现cell的复用。
对于自定义单元格组件,我门既可以通过继承 UITableViewCell,使用纯代码来实现。也可以配合 XIB 来实现。前面一种方法我原来写过很多样例了,本文介绍后面一种方法。

1,使用xib制作tableView的cell介绍
同纯代码相比,xib实现自定义样式会方便很多。不管布局约束,还是样式的设置,使用xib都很简单。
下面通过xib实现如下的自定义单元格:白底圆角,左侧是标题文本,右侧是图片。
             

2,自定义cell组件的步骤
(1)首先,创建自定义cell类(MyTableViewCell)的时候选中“Also create XIB file

(2)创建完毕后,我们可以看到除了自动生成了一个 swift 文件,还有个对应的 xib 文件

(3)打开xib文件,将其背景色设为Clear Color。向里面拖入一个 View 组件,并设置约束。
注意:设置约束时去掉“Constrain to margins”的勾选,这样做防止自动添加外边距。
                    
  
(4)接着往新增的View组件里面拖入一个 Image View 组件和一个 Lable 组件(lines属性设为2),并添加相关的约束

(5)把xib中新增的三个组件在对应的类中做代码关联。同时在初始化函数 awakeFromNib() 中设置圆角。
import UIKit

class MyTableViewCell: UITableViewCell {

    @IBOutlet weak var customView: UIView!
    
    @IBOutlet weak var customLabel: UILabel!
    
    @IBOutlet weak var customImage: UIImageView!
    
    override func awakeFromNib() {
        super.awakeFromNib()

        //设置cell是有圆角边框显示
        customView.layer.cornerRadius = 8
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }    
}

3,自定义cell组件的使用
import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var tableData = [["title":"Swift - 让标签栏按钮UITabBarItem图片居中","image":"img1.png"],
    ["title":"Swift - 使用SSZipArchive实现文件的压缩、解压缩","image":"img2.png"],
    ["title":"Swift - 使用LINQ操作数组/集合","image":"img3.png"],
    ["title":"Swift - 给表格UITableView添加索引功能","image":"img4.png"],
    ["title":"Swift - 列表项尾部附件点击响应","image":"img5.png"],
    ["title":"Swift - 自由调整图标按钮中的图标和文字位置","image":"img6.png"]]
    
    var tableView:UITableView?
    
    override func loadView() {
        super.loadView()
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //创建表视图
        self.tableView = UITableView(frame: UIScreen.mainScreen().applicationFrame,
            style:.Plain)
        self.tableView!.delegate = self
        self.tableView!.dataSource = self
        //设置表格背景色
        self.tableView!.backgroundColor = UIColor(red: 0xf0/255, green: 0xf0/255,
            blue: 0xf0/255, alpha: 1)
        //去除单元格分隔线
        self.tableView!.separatorStyle = .None
        
        //创建一个重用的单元格
        self.tableView!.registerNib(UINib(nibName:"MyTableViewCell", bundle:nil),
            forCellReuseIdentifier:"myCell")
        
        self.view.addSubview(self.tableView!)
    }
    
    //在本例中,只有一个分区
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1;
    }
    
    //返回表格行数(也就是返回控件数)
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.tableData.count
    }
    
    //单元格高度
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath)
        -> CGFloat {
        return 100
    }
    
    //创建各单元显示内容(创建参数indexPath指定的单元)
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
        -> UITableViewCell
    {
        let cell:MyTableViewCell = tableView.dequeueReusableCellWithIdentifier("myCell")
            as! MyTableViewCell
        let item = tableData[indexPath.row]
        cell.customLabel.text = item["title"]
        cell.customImage.image = UIImage(named:item["image"]!)
        return cell
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
源码下载:hangge_1040.zip
评论8
  • 8楼
    2017-02-23 23:07
    布袋

    在xib里的自定义的“+号” 的button事件如何处理呢?
    (如:http://www.hangge.com/blog/cache/detail_1500.html#)
    看了有的技术文章是通过Model传到自定义cell去处理button事件,
    航歌兄,如果您有时间不仿多增加这个功能,因为现在购物车(商城)几乎都要处理BUTTON事件,
    如有打扰,请见谅!

    站长回复

    可以通过把Model传进去处理,个人也喜欢使用NotificationCenter通知,这样耦合小些。即在点击button的时候发送个通知出来,外面进行处理。 

  • 7楼
    2017-02-06 17:25
    yuesf

    self.tableView!.registerNib(UINib(nibName:"MyTableViewCell", bundle:nil), forCellReuseIdentifier:"myCell")
    这句 MyTableViewCell 和 myCell 分别在哪里设置?? 咋在代码中没有看到设置的地方?

    站长回复

    1,MyTableViewCell就是你自定义的cell类名,也就是xib文件名
    2,self.tableView!.registerNib(UINib(nibName:"MyTableViewCell", bundle:nil), forCellReuseIdentifier:"myCell")  这里就是给单元格设置个重用的Identifier(叫myCell)。
    后面的cellForRowAtIndexPath方法中会根据myCell这个名字获取单元格进行复用。

  • 6楼
    2016-07-20 10:00
    落叶

    航哥,如果我在cell里添加一个按钮,点击按钮改变cell中的Image,我发现改变的image不止一个,怎样解决这个问题

    站长回复

    估计你写法有问题。以本文样例为例,如果在cell里添加一个按钮。点击这个按钮后,就要更新表格数据(tableData)里相应值,重新刷新表格就好了。

  • 5楼
    2016-07-11 22:29
    Qiang

    站长你好,新人问下像label这种不能直接创建xib文件有办法实现复用吗?

    站长回复

    当然可以复用,只不过没有对应的xib文件罢了,其他都一样。

  • 4楼
    2016-06-08 15:04
    少说多做

    self.tableView = UITableView(frame: UIScreen.mainScreen().applicationFrame,
    style:.Plain)
    调用这一句时,运行提示:
    ['applicationFrame'] was deprecated in ios 9.0 Use -[UIScreen bounds]

    请问航哥这是什么原因,初学者

    站长回复

    iOS9开始,UIScreen.mainScreen().applicationFrame 这个要废除掉了。你可以改用UIScreen.mainScreen().bounds设置尺寸,或者通过CGRectMake方法自定义尺寸。

  • 3楼
    2016-05-20 22:27
    红领巾

    航哥说下用xib自定义cell和直接用storyboard自定义的优缺点.毕竟用storyboard能少写很多代码

    站长回复

    开头有说啊,使用xib自定义cell可以很方便的实现复用。比如你storyboard里有很多个包含tableView的页面(新闻列表页、收藏列表页等等),它们如果用的是同一个样式的单元格,那么用xib实现就可以很方便地复用。而不用在storyboard中每个页面都要再自定义一遍。

  • 2楼
    2016-03-30 21:57
    尼美的夏天

    航哥,我用xib自定义的cell,代理都设置了,numberOfRowsInSection这个代理方法被执行了,返回值是2,是有值的,在里面打印了东西,可以确定。但是cellForRowAtIndexPath这个方法就是不能执行。 我网上查的,全是说代理没设置啊,numberOfRowsInSection返回为0什么的,这些我都没问题,但cellForRowAtIndexPath就是无法调用。网上还有一种说法是autolayout没有设置好,但是我设置了auto layout之后还是不行。 求航哥解答啊!! 急~

    站长回复

    numberOfRowsInSection如果被执行了,而且还返回值是2,说明代理设置是正确的。
    cellForRowAtIndexPath没调用我觉还是auto layout的问题,或者tableView的frame未设置或设置有误。

  • 1楼
    2016-01-27 10:20
    mingricha

    感谢站长~终于发现自己的错误在哪里了

    站长回复

    不客气,很高兴能帮上你。