用Swift从nib创建自定义的UITableViewCell

163

我正试图从一个nib文件创建自定义的表格视图单元格。我参考了这篇文章(链接)。我遇到了两个问题。

我创建了一个.xib文件,并将UITableViewCell对象拖到上面。我创建了UITableViewCell子类,并将其设置为单元格的类,同时将Cell作为可重用标识符。

import UIKit

class CustomOneCell: UITableViewCell {

    @IBOutlet weak var middleLabel: UILabel!
    @IBOutlet weak var leftLabel: UILabel!
    @IBOutlet weak var rightLabel: UILabel!

    required init(coder aDecoder: NSCoder!) {
        super.init(coder: aDecoder)
    }

    override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    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
    }

}

我在UITableViewController中有如下代码:

import UIKit

class ViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {

    var items = ["Item 1", "Item2", "Item3", "Item4"]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK: - UITableViewDataSource
    override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }

    override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
        let identifier = "Cell"
        var cell: CustomOneCell! = tableView.dequeueReusableCellWithIdentifier(identifier) as? CustomOneCell
        if cell == nil {
            tableView.registerNib(UINib(nibName: "CustomCellOne", bundle: nil), forCellReuseIdentifier: identifier)
            cell = tableView.dequeueReusableCellWithIdentifier(identifier) as? CustomOneCell
        }

        return cell
    }
}

这段代码没有任何错误,但是当我在模拟器中运行它时,它看起来是这样的。

enter image description here

在Storyboard中的UITableViewController中,我没有对单元格做任何操作。空白标识符和没有子类。我尝试将 Cell 标识符添加到原型单元格中再次运行它,但是我得到了相同的结果。

我面临的另一个错误是,当我尝试在UITableViewController中实现以下方法时。

override func tableView(tableView: UITableView!, willDisplayCell cell: CustomOneCell!, forRowAtIndexPath indexPath: NSIndexPath!) {

    cell.middleLabel.text = items[indexPath.row]
    cell.leftLabel.text = items[indexPath.row]
    cell.rightLabel.text = items[indexPath.row]
}

如我所提到的文章中所示,我将cell参数的类型从UITableViewCell更改为CustomOneCell,它是我的UITableViewCell的子类。但是我收到了以下错误信息:

重写方法与选择器'tableView:willDisplayCell:forRowAtIndexPath:'具有不兼容的类型'(UITableView!,CustomOneCell!,NSIndexPath!)->()'

有没有人知道如何解决这些错误?这些在Objective-C中似乎工作正常。

谢谢。

编辑:我刚注意到如果我将模拟器的方向更改为横向,然后将其转回纵向,单元格会出现!我仍然无法弄清楚发生了什么。如果您有时间快速查看,我上传了一个Xcode项目在这里演示问题。

12个回答

241

如果您使用的是Swift 5和iOS 12.2,可以尝试以下代码来解决问题:

CustomCell.swift

import UIKit

class CustomCell: UITableViewCell {

    // Link those IBOutlets with the UILabels in your .XIB file
    @IBOutlet weak var middleLabel: UILabel!
    @IBOutlet weak var leftLabel: UILabel!
    @IBOutlet weak var rightLabel: UILabel!

}

表格视图控制器.swift

import UIKit

class TableViewController: UITableViewController {

    let items = ["Item 1", "Item2", "Item3", "Item4"]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: "CustomCell")
    }

    // MARK: - UITableViewDataSource

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell

        cell.middleLabel.text = items[indexPath.row]
        cell.leftLabel.text = items[indexPath.row]
        cell.rightLabel.text = items[indexPath.row]

        return cell
    }

}
下面的图片显示了一组与提供的代码一起使用且没有Xcode的约束模糊消息的约束条件:

enter image description here


2
谢谢您的回复。但那也没用。我需要在表视图控制器中做出任何更改吗?因为它仍然设置为原型单元格。 - Isuru
1
继续使用原型单元格。但是请确保您已经设置了良好的自动布局约束(如果您使用自动布局)。 - Imanou Petit
1
我上传了一个测试项目在这里,演示了我遇到的问题。如果您有时间,能否请看一下? - Isuru
1
你的测试项目证实了这一点:在你的.xib文件中为自定义单元格设置了一些自动布局约束后,我成功地使你的应用程序正常工作。如果你想了解更多关于自动布局的信息,请查看这个视频 - Imanou Petit
2
@KirillKudaev 在 Swift 4 中它没有被重命名,而是在 Swift 3 中:我已经修复了你的编辑。 - Cœur
显示剩余6条评论

34
这是我的方法,使用Swift 2和Xcode 7.3,这个例子将使用一个ViewController来加载两个.xib文件--一个用于UITableView,一个用于UITableCellView。

enter image description here

在这个例子中,你可以将一个UITableView直接放到一个空的TableNib.xib文件中。在里面,将文件所有者设置为你的ViewController类,并使用outlet引用tableView。

enter image description here

并且

enter image description here

现在,在您的视图控制器中,您可以像往常一样委托tableView,如下所示。
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    ...

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        // Table view delegate
        self.tableView.delegate = self
        self.tableView.dataSource = self

        ...

要创建自定义单元格,您需要将一个表格视图单元格对象拖放到一个空的 TableCellNib.xib 文件中。这一次,在单元格 .xib 文件中,您不需要指定一个“所有者”,但您需要指定一个自定义类和一个标识符,例如“TableCellId”。

enter image description here enter image description here

使用以下方式创建您需要的任何插座的子类:
class TableCell: UITableViewCell {

    @IBOutlet weak var nameLabel: UILabel!

}

最后...在您的视图控制器中,您可以像这样加载和显示整个内容
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    // First load table nib
    let bundle = NSBundle(forClass: self.dynamicType)
    let tableNib = UINib(nibName: "TableNib", bundle: bundle)
    let tableNibView = tableNib.instantiateWithOwner(self, options: nil)[0] as! UIView

    // Then delegate the TableView
    self.tableView.delegate = self
    self.tableView.dataSource = self

    // Set resizable table bounds
    self.tableView.frame = self.view.bounds
    self.tableView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]

    // Register table cell class from nib
    let cellNib = UINib(nibName: "TableCellNib", bundle: bundle)
    self.tableView.registerNib(cellNib, forCellReuseIdentifier: self.tableCellId)

    // Display table with custom cells
    self.view.addSubview(tableNibView)

}

代码展示了如何简单地加载和显示一个nib文件(表格),以及如何注册一个nib供单元格使用。
希望这有所帮助!

2
你能解释一下这行代码中的“tableCellId”是什么吗?因为你没有定义它,而且在xib中也没有手动定义标识符的选项。self.tableView.registerNib(cellNib, forCellReuseIdentifier: self.tableCellId) - PRADIP KUMAR
1
在界面构建器中,当您创建tableCell时,在“属性检查器”中定义标识符。相同的标识符是您在控制器中用于引用对象的标识符。let tableCellId =“myAwesomeCell”。我添加了另一张图片来帮助您。 - internet-nico

22

Swift 4

注册 Nib

override func viewDidLoad() {
    super.viewDidLoad()
    tblMissions.register(UINib(nibName: "MissionCell", bundle: nil), forCellReuseIdentifier: "MissionCell")
}
在TableView中的数据源。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: "MissionCell", for: indexPath) as? MissionCell else { return UITableViewCell() }
    return cell
}

19

使用屏幕截图详细解决方案

  1. 创建一个空的用户界面文件,并将其命名为MyCustomCell.xib

图片描述

  1. UITableViewCell添加为xib文件的根视图,以及您想要的任何其他可视组件。

图片描述

  1. 创建一个名为MyCustomCell的cocoa touch类文件,作为UITableViewCell的子类。

图片描述 图片描述

  1. 设置自定义类和重用标识符,用于自定义表格视图单元格。

图片描述 图片描述

  1. 打开助理编辑器并使用ctrl+拖动为您的可视组件创建插座。

图片描述

  1. 配置一个UIViewController以使用您的自定义单元格。
class MyViewController: UIViewController {

    @IBOutlet weak var myTable: UITableView!

    override func viewDidLoad {
        super.viewDidLoad()

        let nib = UINib(nibName: "MyCustomCell", bundle: nil)
        myTable.register(nib, forCellReuseIdentifier: "MyCustomCell")
        myTable.dataSource = self
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let cell = tableView.dequeueReusableCell(withIdentifier: "MyCustomCell") as? MyCustomCell {
            cell.myLabel.text = "Hello world."
            return cell
        }
        ...
    }
}


你救了我的一天。我曾经复制了 MyHeaderView.swift 用于自定义单元格。然而,头视图的 .swift 文件在 属性检查器 中的 表格视图单元格 中没有 标识符。因此...运行时错误发生了。 - mazend
顺便问一下,为什么在 .swifttableView?.register(blahblah, forCellReuseIdentifier: "myCell") 中我们需要声明相同的标识符名称?我以为其中一个是不必要的,但是我发现两者都是必要的。 - mazend
嗯,可能是因为自定义单元格的 .xib 文件可以包含多个 UITableViewCell,所以仅凭 .xib 文件无法找到正确的单元格。 - mazend
这个解决方案完全符合我的要求,非常感谢。 - thevikasnayak

6

Swift 4.1.2

XIB。

创建ImageCell2.swift。

步骤1。

import UIKit

class ImageCell2: UITableViewCell {

    @IBOutlet weak var imgBookLogo: UIImageView!
    @IBOutlet weak var lblTitle: UILabel!
    @IBOutlet weak var lblPublisher: UILabel!
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

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

}

步骤2. 根据ViewController类进行查看。
  import UIKit

    class ImageListVC: UIViewController,UITableViewDataSource,UITableViewDelegate {
    @IBOutlet weak var tblMainVC: UITableView!

    var arrBook : [BookItem] = [BookItem]()

    override func viewDidLoad() {
        super.viewDidLoad()
         //Regester Cell
        self.tblMainVC.register(UINib.init(nibName: "ImageCell2", bundle: nil), forCellReuseIdentifier: "ImageCell2")
        // Response Call adn Disply Record
        APIManagerData._APIManagerInstance.getAPIBook { (itemInstance) in
            self.arrBook = itemInstance.arrItem!
            self.tblMainVC.reloadData()
        }
    }
    //MARK: DataSource & delegate
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.arrBook.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//    [enter image description here][2]
        let cell  = tableView.dequeueReusableCell(withIdentifier: "ImageCell2") as! ImageCell2
        cell.lblTitle.text = self.arrBook[indexPath.row].title
        cell.lblPublisher.text = self.arrBook[indexPath.row].publisher
        if let authors = self.arrBook[indexPath.row].author {
            for item in authors{
                print(" item \(item)")
            }
        }
        let  url  = self.arrBook[indexPath.row].imageURL
        if url == nil {
            cell.imgBookLogo.kf.setImage(with: URL.init(string: ""), placeholder: UIImage.init(named: "download.jpeg"))
        }
        else{
            cell.imgBookLogo.kf.setImage(with: URL(string: url!)!, placeholder: UIImage.init(named: "download.jpeg"))
        }
        return cell
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 90
    } 

}

5

您没有按照以下方式注册您的nib:

tableView.registerNib(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: "CustomCell")

4

另一种可能对您有用的方法(这是我使用的方法)是注册一个类。

假设您创建了一个自定义tableView,如下所示:

class UICustomTableViewCell: UITableViewCell {...}

您可以使用 "registerClass" 将此单元格注册到您将在其中显示它的任何 UITableViewController 中:

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.registerClass(UICustomTableViewCell.self, forCellReuseIdentifier: "UICustomTableViewCellIdentifier")
}

您可以按照预期在行单元格方法中调用它:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("UICustomTableViewCellIdentifier", forIndexPath: indexPath) as! UICustomTableViewCell
    return cell
}

3
为了解决"Overriding method... has incompatible type..."错误,我已经更改了函数声明为:
override func tableView(tableView: (UITableView!), 
                        cellForRowAtIndexPath indexPath: (NSIndexPath!)) 
    -> UITableViewCell {...}

(原文是-> UITableViewCell!,末尾有感叹号)

2

我必须确保在创建输出口时指定连接到单元格,而不是对象的所有者。当菜单出现时,您必须在“对象”下拉菜单中选择它进行命名。当然,您还必须将单元格声明为您的类,而不仅仅是“TableViewCellClass”。否则,我会一直收到“类不符合关键字”的错误。


2

这行代码在 TableView 单元格中添加:

static var nib  : UINib{
    return UINib(nibName: identifier, bundle: nil)
}

static var identifier : String{
    return String(describing: self)    
}

    

请在视图控制器中进行注册,像这样:

这行代码用于 viewDidLoad 中。

tableview.register(TopDealLikedTableViewCell.nib, forCellReuseIdentifier: TopDealLikedTableViewCell.identifier)

在indexpath处的行中的单元格

if let cell = tableView.dequeueReusableCell(withIdentifier:
    TopDealLikedTableViewCell.identifier) as? TopDealLikedTableViewCell{
    return cell
}

return UITableViewCell()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接