【问题标题】:Custom UITableViewCell from nib in Swift从 Swift 中的 nib 自定义 UITableViewCell
【发布时间】:2014-10-21 21:38:36
【问题描述】:

我正在尝试从笔尖创建自定义表格视图单元格。我指的是这篇文章here。我面临两个问题。

我创建了一个带有 UITableViewCell 对象的 .xib 文件。我创建了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
    }
}

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

在情节提要的 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 项目 here 演示了这个问题,如果你有时间快速浏览一下。

【问题讨论】:

    标签: ios uitableview swift ios8


    【解决方案1】:

    使用 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!
    
    }
    

    TableViewController.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 的任何约束歧义消息:

    【讨论】:

    • 感谢您的回复。但这也没有用。我需要更改表格视图控制器中的任何内容吗?因为它仍然设置为原型单元格。
    • 继续使用原型单元。但请确保您设置了良好的自动布局约束(如果您使用自动布局)。
    • 我上传了一个测试项目here,展示了我遇到的问题。有时间可以看看吗?
    • 您的测试项目证实了这一点:在您为 .xib 文件中的自定义单元格设置一些自动布局约束后,我能够使您的应用程序正常工作。如果您需要了解更多关于自动布局的信息,请查看this video
    • @KirillKudaev 它没有在 Swift 4 中重命名,而是在 Swift 3 中:我已经修复了您的编辑。
    【解决方案2】:

    这是我使用 Swift 2 和 Xcode 7.3 的方法。此示例将使用单个 ViewController 加载两个 .xib 文件——一个用于 UITableView,一个用于 UITableCellView。

    对于本示例,您可以将 UITableView 直接放入一个空的 TableNib.xib 文件中。在内部,将 文件的所有者 设置为您的 ViewController 类,并使用插座来引用 tableView。

    现在,在您的视图控制器中,您可以像往常一样委托 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 文件中,您不必指定“所有者”,但您需要指定 Custom Classidentifier,如“TableCellId”

    使用您需要的任何网点创建您的子类

    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 以供单元格使用。

    希望对你有帮助!!!

    【讨论】:

    • 你能解释一下这一行中的“tableCellId”是什么...... self.tableView.registerNib(cellNib, forCellReuseIdentifier: self.tableCellId)......因为你还没有定义那是什么。而且您不能在 xib 中手动定义标识符.. 没有选项可以定义它
    • 在界面构建器中,当您创建 tableCell 时,在“属性检查器”中定义一个标识符。您在控制器中使用相同的标识符来引用对象。 let tableCellId = "myAwesomeCell"。我添加了另一张图片来帮助您。
    【解决方案3】:

    斯威夫特 4

    注册笔尖

    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
    }
    

    【讨论】:

      【解决方案4】:

      带截图的详细解决方案

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

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

      1. 创建一个类名为MyCustomCell 的可可触摸类文件作为UITableViewCell 的子类。

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

      1. 打开助手编辑器和ctrl+drag 为您的视觉组件创建插座。

      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。标题视图的.swiftAttribute Inspector 中的Table View Cell 中没有identifier。所以...发生了运行时错误。
      • 顺便说一句.. 为什么我们在.swifttableView?.register(blahblah, forCellReuseIdentifier: "myCell") 中声明了相同的标识符名称?我认为其中一个不是必需的,但是..我发现两者都是必不可少的。
      • 嗯..这可能是因为...xib自定义单元格可以包含多个UITableViewCell所以...xib不足以..找到正确的单元格。
      【解决方案5】:

      您没有按以下方式注册您的笔尖:

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

      【讨论】:

        【解决方案6】:

        另一种可能对你有用的方法(我就是这样做的)是注册一个类。

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

        class UICustomTableViewCell: UITableViewCell {...}
        

        然后,您可以在任何 UITableViewController 中注册此单元格,您将使用“registerClass”在其中显示它:

        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
        }
        

        【讨论】:

          【解决方案7】:

          为了修复“覆盖方法...具有不兼容的类型...”错误,我已将函数声明更改为

          override func tableView(tableView: (UITableView!), 
                                  cellForRowAtIndexPath indexPath: (NSIndexPath!)) 
              -> UITableViewCell {...}
          

          (原为-> UITableViewCell! -- 末尾有感叹号)

          【讨论】:

            【解决方案8】:

            斯威夫特 4.1.2

            xib.

            创建 ImageCell2.swift

            第一步

            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
                } 
            
            }
            

            【讨论】:

              【解决方案9】:

              我必须确保在创建出口时指定我正在连接到单元格,而不是对象的所有者。当菜单出现命名时,您必须在“对象”下拉菜单中选择它。当然,您也必须将单元格声明为您的类,而不仅仅是“TableViewCellClass”。否则我会一直让这个类不符合密钥。

              【讨论】:

                【解决方案10】:

                简单地使用类 UITableViewCell 的 xib。根据要求设置 UI 并分配 IBOutlet。在表格视图的 cellForRowAt() 中使用它,如下所示:

                //MARK: - table method
                
                func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                    return self.arrayFruit.count
                }
                func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                    var cell:simpleTableViewCell? = tableView.dequeueReusableCell(withIdentifier:"simpleTableViewCell") as? simpleTableViewCell
                    if cell == nil{
                        tableView.register(UINib.init(nibName: "simpleTableViewCell", bundle: nil), forCellReuseIdentifier: "simpleTableViewCell")
                        let arrNib:Array = Bundle.main.loadNibNamed("simpleTableViewCell",owner: self, options: nil)!
                        cell = arrNib.first as? simpleTableViewCell
                    }
                
                    cell?.labelName.text = self.arrayFruit[indexPath.row]
                    cell?.imageViewFruit.image = UIImage (named: "fruit_img")
                
                    return cell!
                
                }
                func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
                {
                 return 100.0
                }
                

                100% 正常工作(已测试)

                【讨论】:

                • 它说控制单元格?.labelName 是 nil
                【解决方案11】:

                在单元格上设置

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

                【讨论】:

                  【解决方案12】:

                  这一行加入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)
                  

                  索引路径处的行单元格

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多