IOS TableViewl详解(两种Cell注册方式)

学习iOS第二周,学习到了比较重要的组件TableView,照着书上敲,没有实现效果。在同事的帮助下,有了比较清晰的理解,记录在此。

我使用的完全是xib的方式。我们先按照这种方式来梳理出Demo

先看下项目结构目录

IOS TableViewl详解(两种Cell注册方式)

先从最简单的model开始看起,可以理解为javabean

//
//  News.swift
//  HelloIOS
//
//  Created by zxg on 2018/9/27.
//  Copyright © 2018年 zxg. All rights reserved.
//

import UIKit

class News: NSObject {
    
    var title:String = "" //新闻标题
    var desc:String = "" //新闻描述
    
    init(title:String,desc:String){
        self.title = title
        self.desc = desc
    }


}

接下来是cell

在NewsViewCell.xib中,引入两个lib,并关联到NewsViewCell.swift中,比较简单,直接上截图:
 

IOS TableViewl详解(两种Cell注册方式)

我们拖拽了两个label,一个用于显示title,一个用于显示描述

接下来是重要的Controller,有两种方案,第一种自己编写的Controller继承自UIViewController,自己去实现UITableView的两个委托,并手动在xib文件中拖拽tableview并关联。第二种是自己编写的Controller直接继承系统提供的UITableViewController,这种方案比较省事,一来,不用手动实现委托,二来xib中会自带tableview,不需要再手动关联。这里我们先放下系统提供的UITableViewController

import Foundation
import UIKit
import _SwiftUIKitOverlayShims

//
//  UITableViewController.h
//  UIKit
//
//  Copyright (c) 2008-2017 Apple Inc. All rights reserved.
//

// Creates a table view with the correct dimensions and autoresizing, setting the datasource and delegate to self.
// In -viewWillAppear:, it reloads the table's data if it's empty. Otherwise, it deselects all rows (with or without animation) if clearsSelectionOnViewWillAppear is YES.
// In -viewDidAppear:, it flashes the table's scroll indicators.
// Implements -setEditing:animated: to toggle the editing state of the table.

@available(iOS 2.0, *)
open class UITableViewController : UIViewController, UITableViewDelegate, UITableViewDataSource {

    
    public init(style: UITableViewStyle)

    public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)

    public init?(coder aDecoder: NSCoder)

    
    open var tableView: UITableView!

    @available(iOS 3.2, *)
    open var clearsSelectionOnViewWillAppear: Bool // defaults to YES. If YES, any selection is cleared in viewWillAppear:

    
    @available(iOS 6.0, *)
    open var refreshControl: UIRefreshControl?
}

可以看到继承自UIViewController并实现委托,同时存在变量tableView。我们可以直接使用

接下来,放下我的Controller的代码,很多关键点都在注释中说明了

//
//  NewsViewController.swift
//  HelloIOS
//
//  Created by zxg on 2018/9/26.
//  Copyright © 2018年 zxg. All rights reserved.
//

import UIKit

class NewsViewController: UITableViewController {
    
    //模拟数据源
    let newsItem = [News(title: "吴秀波出轨", desc: "吴秀波圈养小妾"),News(title: "中美贸易升级", desc: "美提升2000亿关税"),News(title: "海底捞上市", desc: "创始人张勇暴富"),News(title: "全国喜迎十一长假", desc: "国庆黄金周即将来临"),News(title: "全国喜迎十一长假", desc: "国庆黄金周即将来临"),News(title: "全国喜迎十一长假", desc: "国庆黄金周即将来临"),News(title: "全国喜迎十一长假", desc: "国庆黄金周即将来临"),News(title: "全国喜迎十一长假", desc: "国庆黄金周即将来临"),News(title: "全国喜迎十一长假", desc: "国庆黄金周即将来临"),News(title: "全国喜迎十一长假", desc: "国庆黄金周即将来临"),News(title: "全国喜迎十一长假", desc: "国庆黄金周即将来临")]
    let cellIdentifier = "NewsCell"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //将TableView背景横线去掉
        self.tableView.separatorStyle = UITableViewCellSeparatorStyle.none
        
        //两种register方式:
        //通过UINib注册,适用于xib方式,本例中,cell采用的是xib方式而非纯代码。
        let nib = UINib.init(nibName: "NewsViewCell", bundle: nil)
        tableView.register(nib, forCellReuseIdentifier: cellIdentifier)
        
        /*通过class注册,适用于纯代码方式,需要在cell文件中重写init(style: UITableViewCellStyle, reuseIdentifier: String?)
          在其中初始化view
        */
        //tableView.register(NewsViewCell.self, forCellReuseIdentifier: cellIdentifier)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: - Table view data source
    //UITableViewDataSource委托协议:返回小节的数量
    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }
    //UITableViewDataSource委托协议:返回每个小节的item数量,section为小节Index
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return newsItem.count
    }

    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        /*使用dequeueReusableCell(withIdentifier: cellIdentifier)方法获取cell不需要通过register注册,但是要判断cell是否为空,为空则new cell
        var cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) as? NewsViewCell

        if(cell == nil){
            cell = NewsViewCell(style: UITableViewCellStyle.default, reuseIdentifier: cellIdentifier)
        } */

        /*使用dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath)获取cell,其实就使用了tableview的缓存机制,当取到cell为空时
          系统会帮忙创建register的cell,所以就不需要我们手动判空并实例化cell。所以必须register
        */
        // Configure the cell...
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? NewsViewCell
        let row = indexPath.row
        let news:News = newsItem[row]
        cell?.tvNewsTitle?.text = news.title
        cell?.tvNewsDesc?.text = news.desc
        return cell!
    
    }
    //返回每个cell的高度
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100.0
    }
    
}

补一张NewsViewCell.swift的代码,其中也包含了一些注释:

//
//  NewsViewCell.swift
//  HelloIOS
//
//  Created by zxg on 2018/9/26.
//  Copyright © 2018年 zxg. All rights reserved.
//

import UIKit

class NewsViewCell: UITableViewCell {

    @IBOutlet weak var tvNewsTitle: UILabel!
    @IBOutlet weak var tvNewsDesc: UILabel!
    //使用xib方式创建cell,在dequeueReusableCell()时会调用到该方法
    //本例中使用的是xib方式
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

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

        // Configure the view for the selected state
    }
    
    //使用纯代码方式创建cell,在dequeueReusableCell()时会调用到该方法
    //需要在该方法中创建view
//    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        //例如创建如下view:
//        super.init(style: style, reuseIdentifier: reuseIdentifier)
//        self.iconImageView=UIImageView()
//        self.contentView.addSubview(self.iconImageView!)
//        self.TitleLabel=UILabel()
//        self.contentView.addSubview(self.TitleLabel!)
//        setUpviews()
//    }
    
    
}

关于纯代码创建cell,这篇文章代码可看:https://www.cnblogs.com/small-Sun/p/5667764.html

关于UITableView复用机制理解:https://blog.csdn.net/wuzesong/article/details/52225487

总结:

1、dequeueReuseableCellWithIdentifier:与dequeueReuseableCellWithIdentifier:forIndexPath:的区别:
前者不必向tableView注册cell的Identifier,但需要判断获取的cell是否为nil;
后者则必须向table注册cell,可省略判断获取的cell是否为空,因为无可复用cell时runtime将使用注册时提供的资源去新建一个cell并返回。
2、自定义cell时,记得将其他内容加到self.contentView 上,而不是直接添加到 cell 本身上
总结:
1.自定义cell时,
若使用nib,使用 registerNib: 注册,dequeue时会调用 cell 的 -(void)awakeFromNib
不使用nib,使用 registerClass: 注册, dequeue时会调用 cell 的 - (id)initWithStyle:withReuseableCellIdentifier:
2.需不需要注册?
使用dequeueReuseableCellWithIdentifier:可不注册,但是必须对获取回来的cell进行判断是否为空,若空则手动创建新的cell;
使用dequeueReuseableCellWithIdentifier:forIndexPath:必须注册,但返回的cell可省略空值判断的步骤。

写在最后:

本文介绍内容比较浅显,没有深究例如tableView缓存机制,xib讲解等。是因为我还没深入学习到。会在后边的文章接受