ios 在 Swift 中使用以编程方式创建的 UITableViewCell 子类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27585409/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 04:04:40  来源:igfitidea点击:

Using a programmatically created UITableViewCell subclass in Swift

iosuitableviewswift

提问by Jeffrey

I'm working on a table view with a custom cell that has been created programmatically, with no usage of IB. I've looked around on Google and asked around in NSChat, but I still can't get it to work. It just displays the default table view. Thanks in advance!

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

EventCell.swift

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

视图控制器.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


    }
}

采纳答案by drewag

Ok first a few important comments.

好的,首先是一些重要的评论。

First, you are needlessly creating your own new cell every time the table view requests a cell instead of reusing old cells. You should remove this line:

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

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

That is unnecessary because dequeue will automatically create new cells as needed based on the class you have registered for the given identifier.

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

Second, you should not be using the main screen bounds when laying out your code. This will break down if your table view is not the full width. Instead, you can use self.boundsso it is always relative to the cell itself.

其次,在布置代码时不应使用主屏幕边界。如果您的表格视图不是全宽,这将崩溃。相反,您可以使用self.boundsso 它始终相对于单元格本身。

Third, you should not be calling setNeedsLayout or layoutIfNeeded because if that method is called, it is already laying out everything again.

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

Fourth, you should register your table view cell class before setting the table view data source just in case UITableView every starts requesting things from data source when the data source is set.

第四,你应该在设置表格视图数据源之前注册你的表格视图单元格类,以防万一UITableView在设置数据源时开始从数据源请求东西。

Fifth, two of your subviews have a size of 0,0 so they are not going to show up anyway.

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

If this code is indeed running without crashing then you are creating EventCells because you are doing a forced casting from the result of the dequeueReusableCellWithIdentifier:forIndexPath. That means you simply have a layout / display / data issue.

如果此代码确实在没有崩溃的情况下运行,那么您正在创建 EventCells,因为您正在根据dequeueReusableCellWithIdentifier:forIndexPath. 这意味着您只是存在布局/显示/数据问题。

回答by Suragch

I had the same question. I applied the advice in @drewag's answer, but there were some additional issues. Below is a bare-bones, proof-of-concept example that shows how to use a programmatically created UITableViewCellsubclass.

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

The image below is what it should look like. The yellow rectangles are UILabelsubviews in the custom cells.

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

enter image description here

在此处输入图片说明

Code

代码

Here is the UITableViewCell subclass. It's job is to initialize the cell and its content, add the subviews, and layout the subviews.

这是 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)
    }
}

Here is the view controller code.

这是视图控制器代码。

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

Notes:

笔记:

  • I used a regular UIViewControllerrather than a UITableViewController. If you use a UITableViewControllerthen remove the UITableViewDelegateand UITableViewDataSourceprotocol references since these are redundant for a UITableViewController. You also won't need the @IBOutlet tableViewline.
  • The main problem line I found in the original question was eventName = UILabel(frame: CGRectMake(...)). Instead it should have been eventName.frame = CGRect(...)
  • 我使用的是常规UIViewController而不是UITableViewController. 如果您使用 aUITableViewController则删除UITableViewDelegateUITableViewDataSource协议引用,因为这些对于UITableViewController. 你也不需要这@IBOutlet tableView条线。
  • 我在原始问题中发现的主要问题是eventName = UILabel(frame: CGRectMake(...)). 相反,它应该是eventName.frame = CGRect(...)

回答by Cheyne Mathey-Owens

Your particular code does not work because it is creating new UILabels in EventCell.layoutSubviews. Only the initial UILabels are getting added to the view tree and their sizes are probably 0'd.

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

I'm guessing you meant to update their frames, e.g. update the EventCell class method:

我猜你打算更新他们的框架,例如更新 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>)
}

回答by johndpope

You can use lazy instantiation in swift to avoid having to drop into layoutSubviews / view life cycle dance.

您可以在 swift 中使用延迟实例化,以避免不得不陷入 layoutSubviews / view 生命周期舞蹈。

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

}