问题
我正在创建可重用的自定义UITableViewCell,然后将其子类化以供重用。如何设置这些各种子类以链接到相同的xib文件?
背景
让我们调用我的自定义UITableViewCell PickerTableViewCell
。该单元格包括UIPickerView
以及有关选取器视图外观和行为的所有实现。当我要使用此单元格时,我唯一需要提供的是选择器视图的数据。因此,我继承了PickerTableViewCell
的子类,然后只需创建所需的数据源并将其分配给选择器视图。到目前为止,一切都很好。
以下是PickerTableViewCell
的相关部分:
class PickerTableViewCell: UITableViewCell {
var picker: UIPickerView! = UIPickerView()
var pickerDataSource: PickerViewDataSource!
override func awakeFromNib() {
super.awakeFromNib()
self.picker = UIPickerView(frame: CGRect(x: 0, y: 40, width: 0, height: 0))
self.picker.delegate = self
self.assignPickerDataSource()
}
// Must be overriden by child classes
func assignPickerDataSource() {
fatalError("Must Override")
}
这是一个子类的示例:
class LocationPickerTableViewCell: PickerTableViewCell {
override func assignPickerDataSource() {
self.pickerDataSource = LocationPickerDataSource()
self.picker.dataSource = self.pickerDataSource
}
}
问题
由于我在各地使用这些单元格,并使用了不同的数据源,因此我创建了一个xib文件,该文件定义了单元格的外观PickerTableViewCell.xib
,并将其分配给类PickerTableViewCell
。在要用于它的视图控制器中,我将单元格注册到viewDidLoad()
中的表视图中。然后,在func tableView(_:, cellForRowAt)
内部,使子类我想要的像这样:
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! LocationPickerTableViewCell
return cell
这是发生问题的地方。创建的单元格是PickerTableViewCell
,而不是其子类LocationPickerTableViewCell
。这会遇到致命错误,我将其置于父类中,而子类将其覆盖。
我发现解决此问题的唯一方法是为要创建的每个子类创建一个单独的xib文件,并将其分配给相关的子类。尽管此解决方案有效,但将所有这些xib文件几乎完全相同(除了将它们分配给哪个类)在我的项目中还是很错误的。
有没有一种方法可以克服这个问题,并使所有这些单元链接到同一个单一的xib文件?
谢谢! :)
答案 0 :(得分:1)
将xib加载的视图添加到您要在其中使用的UITableViewCell
类中。
根据您的需求设计创建xib,在您的示例PickerTableViewCell.xib
创建要在其中使用该视图的UITableViewCell
子类。我为此使用FirstTableViewCell
和SecondTableViewCell
。
在表单元格的构造函数中加载xib并将其添加到表单元格中。
let nib = Bundle.main.loadNibNamed("PickerTableViewCell", owner: nil, options: nil)
if let view = nib?.first as? UIView{
self.addSubview(view)
}
如果xib有任何@IBOutlet
,则通过viewWithTag
函数将其获取并分配给类变量
if let label = self.viewWithTag(1) as? UILabel{
self.label = label
}
override
reuseIdentifier
每个具有不同名称的tableviewCell子类的var
override var reuseIdentifier: String?{
return "FirstTableViewCell"
}
现在,您可以根据需要使用这些类,按照以下步骤进行操作:
通过带有表视图的xib注册这个tableviewCell子类:
tableView.register(FirstTableViewCell.self, forCellReuseIdentifier:"PickerTableViewCell")
现在以cellForRowAt indexPath
方法使用它。
var cell = tableView.dequeueReusableCell(withIdentifier: "FirstTableViewCell", for: indexPath) as? FirstTableViewCell
if cell == nil {
cell = FirstTableViewCell()
}
cell?.label?.text = "FirstTableViewCell"
答案 1 :(得分:0)
不要使用子类来分配不同的数据源。
pickerDataSource
中分配tableView(_:cellForRowAt:)
在表格视图控制器中,您需要分配pickerDataSource
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! PickerTableViewCell
cell.pickerDataSource = LocationPickerDataSource()
return cell
}
将pickerDataSource
与didSet
分配后,需要处理其他工作。
class PickerTableViewCell: UITableViewCell {
var picker: UIPickerView! = UIPickerView()
var pickerDataSource: PickerViewDataSource! {
didSet {
self.picker.dataSource = self.pickerDataSource
}
}
…
}
PickerTableViewCell
。这里,不是子类化,而是将所需的逻辑添加到一个在各自的扩展名中定义的唯一命名的设置方法中。
extension PickerTableViewCell {
func setupLocationPickerDataSource() {
self.pickerDataSource = LocationPickerDataSource()
self.picker.dataSource = self.pickerDataSource
}
}
然后在tableView(_:cellForRowAt:)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier") as! PickerTableViewCell
cell.setupLocationPickerDataSource()
return cell
}