【问题标题】:Using a programmatically created UITableViewCell subclass in Swift在 Swift 中使用以编程方式创建的 UITableViewCell 子类
【发布时间】:2014-12-20 23:41:45
【问题描述】:

我正在使用以编程方式创建的自定义单元格的表格视图,不使用 IB。我在 Google 上环顾四周并在 NSChat 中四处询问,但我仍然无法让它工作。它只显示默认的表格视图。提前致谢!

EventCell.swift

import UIKit

class EventCell: UITableViewCell {

    var eventName: UILabel = UILabel()
    var eventCity: UILabel = UILabel()
    var eventTime: UILabel = UILabel()

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

        self.contentView.addSubview(eventName)
        self.contentView.addSubview(eventCity)
        self.contentView.addSubview(eventTime)
    }

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

    override func layoutSubviews() {
        super.layoutSubviews()

        eventName = UILabel(frame: CGRectMake(20, 10, self.bounds.size.width - 40, 25))
        eventCity = UILabel(frame: CGRectMake(0, 0, 0, 0))
        eventTime = UILabel(frame: CGRectMake(0, 0, 0, 0))

    }

}

ViewController.swift

class ViewController: UITableViewController, UITableViewDelegate {

    var events: Dictionary<String, [String]> = ["0": ["Monroe Family", "La Cañada", "8:30"]]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self;
        tableView.delegate = self;

        tableView.registerClass(EventCell.self, forCellReuseIdentifier: "EventCell")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }

    override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return UITableViewAutomaticDimension;
    }

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

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

        var cellIdendifier: String = "EventCell"

        var cell: EventCell = tableView.dequeueReusableCellWithIdentifier(cellIdendifier, forIndexPath: indexPath) as EventCell
        cell = EventCell(style: .Default, reuseIdentifier: cellIdendifier)

        if let i = events[String(indexPath.row)] {
            cell.eventName.text = i[0]
            cell.eventCity.text = i[1]
            cell.eventTime.text = i[2]
        }

        cell.sizeToFit()

        return cell


    }
}

【问题讨论】:

  • 让 cellIdendifier: String = "EventCell" 因为 cellIdentifier 没有发生变异。
  • 让单元格:EventCell = tableView.dequeueReusableCellWithIdentifier(cellIdendifier, forIndexPath: indexPath) as! EventCell,因为单元格未发生突变并使用 ! 强制向下转换

标签: ios uitableview swift


【解决方案1】:

好的,先介绍几个重要的cmets。

首先,每次表格视图请求一个单元格而不是重用旧单元格时,您都不必要地创建自己的新单元格。您应该删除此行:

cell = EventCell(style: .Default, reuseIdentifier: cellIdendifier)

这是不必要的,因为 dequeue 会根据您为给定标识符注册的类根据需要自动创建新单元格。

第二,在布局代码时不应使用主屏幕边界。如果您的表格视图不是全宽,这将崩溃。相反,您可以使用self.bounds,因此它始终与单元格本身相关。

第三,您不应该调用 setNeedsLayout 或 layoutIfNeeded,因为如果调用该方法,它已经在重新布置所有内容了。

第四,您应该在设置表格视图数据源之前注册您的表格视图单元类,以防UITableView在设置数据源时开始向数据源请求内容。

第五,您的两个子视图的大小为 0,0,因此无论如何它们都不会出现。

如果此代码确实在运行而没有崩溃,那么您正在创建 EventCell,因为您正在从 dequeueReusableCellWithIdentifier:forIndexPath 的结果进行强制转换。这意味着您只是有一个布局/显示/数据问题。

【讨论】:

  • 在这种情况下 self.bounds 会和 self.contentView.bounds 一样吗?
  • 您好!我有一个问题,如果我有一个自定义的初始化,带有我自己的参数,你会如何处理这个?那么是不可能使细胞出队的吗?我总是必须创建它们?
  • @PMT 你的自定义初始化应该接受一个重用标识符,然后将它传递给指定的init(style:reuseIdentifier:)。然后你可以像往常一样将它出队。
  • @drewag 但 dequeue 函数不会使用我的自定义函数,如果我手动初始化它,那么 dequeue 不会做任何事情,因为我总是在初始化它,对吧?有没有办法知道出队是在创建一个新的单元格(然后我可以使用我自己的 init)或者它是否正在重用一个单元格(然后我让它成为并且我不做 init)?我认为过去 dequeue 在无法重用单元格时会返回 nil,因此您可以执行类似的操作, if cell == nil { } 但现在它总是返回一个单元格,所以我没有想法
  • @PMT 如果你想要一个自定义初始化器,你应该回退到使用dequeueReusableCell(withIdentifier:)(没有索引路径参数)。这就像你描述的那样工作:如果它想要初始化一个新的单元格,则返回 nil 。这意味着您不再需要提前向 tableview 注册类。
【解决方案2】:

我也有同样的问题。我在@drewag 的回答中应用了建议,但还有一些其他问题。下面是一个简单的概念验证示例,展示了如何使用以编程方式创建的 UITableViewCell 子类。

下图是它应该的样子。黄色矩形是自定义单元格中的UILabel 子视图。

代码

这里是 UITableViewCell 子类。它的工作是初始化单元格及其内容,添加子视图和布局子视图。

import UIKit
class MyCustomCell: UITableViewCell {

    var myLabel = UILabel()

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

        myLabel.backgroundColor = UIColor.yellowColor()
        self.contentView.addSubview(myLabel)
    }

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

    override func layoutSubviews() {
        super.layoutSubviews()

        myLabel.frame = CGRect(x: 20, y: 0, width: 70, height: 30)
    }
}

这是视图控制器代码。

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    let myArray = ["one", "two", "three", "four"]
    let cellReuseIdendifier = "cell"


    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.registerClass(MyCustomCell.self, forCellReuseIdentifier: cellReuseIdendifier)

        tableView.dataSource = self
        tableView.delegate = self
    }

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

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier(cellReuseIdendifier, forIndexPath: indexPath) as! MyCustomCell
        cell.myLabel.text = myArray[indexPath.row]

        return cell
    }
}

注意事项:

  • 我使用了普通的UIViewController 而不是UITableViewController。如果您使用UITableViewController,则删除UITableViewDelegateUITableViewDataSource 协议引用,因为这些对于UITableViewController 来说是多余的。您也不需要@IBOutlet tableView 行。
  • 我在原始问题中发现的主要问题行是eventName = UILabel(frame: CGRectMake(...))。相反,它应该是eventName.frame = CGRect(...)

【讨论】:

    【解决方案3】:

    您的特定代码不起作用,因为它正在 EventCell.layoutSubviews 中创建新的 UILabel。只有最初的 UILabel 被添加到视图树中,它们的大小可能是 0。

    我猜你打算更新他们的框架,例如更新 EventCell 类方法:

    override func layoutSubviews() {
        super.layoutSubviews()
    
        eventName.frame = CGRectMake(20, 10, self.bounds.size.width - 40, 25)
        eventCity.frame = CGRectMake(<actual size>)
        eventTime.frame = CGRectMake(<actual size>)
    }
    

    【讨论】:

      【解决方案4】:

      您可以在 swift 中使用惰性实例化以避免陷入 layoutSubviews / 视图生命周期之舞。

      class EventCell: UITableViewCell {
        lazy public  var lblName = {
          return UILabel (frame: CGRectMake(10, 0, self.bounds.size.width , 40))
          }()
      
       lazy public  var lblCity = {
          return UILabel (frame: CGRectMake(10, 40, self.bounds.size.width , 20))
          }()
       lazy public  var lblTime = {
          return UILabel (frame: CGRectMake(self.bounds.size.width - 80, 10, , 25))
          }()
      
      
        override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
          super.init(style: style, reuseIdentifier: reuseIdentifier)
      
          self.contentView.addSubview(lblName)
          self.contentView.addSubview(lblCity)
          self.contentView.addSubview(lblTime)
        }
      
      } 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-04
        • 1970-01-01
        • 1970-01-01
        • 2014-10-14
        • 1970-01-01
        • 2022-10-31
        相关资源
        最近更新 更多