在表格视图中放置不同的自定义单元格类型
Posted
技术标签:
【中文标题】在表格视图中放置不同的自定义单元格类型【英文标题】:Putting different custom cell types in a tableview 【发布时间】:2018-05-20 15:41:24 【问题描述】:我在尝试使用不同的自定义单元格类型填充单个 tableview 时遇到问题。我试图删除代码中所有不相关的部分。我可能有很多后续问题,但在 dequereusablecell 语句(在 cellforrow 函数中)中,我无法将单元格向下转换为存储在数组 cellTypes 中的类型 FilterTableViewCell 或 FilterTableViewCell2 .我收到错误“数组类型现在用元素类型周围的括号写入”......它认为我试图向下转换为数组类型,而不是数组 cellTypes 的给定索引中的单元格类型。我希望这很清楚。
我还尝试在 switch 语句的每种情况下都放置一个 dequereusablecell 语句,但在我的代码的其他部分出现错误。它基本上将 FIlterTableViewCell 和 FilterTableViewCell2 视为基类类型(UITableViewCell),因此它无法访问这两个子类中的属性。我希望这很清楚。但如果您发现我的代码的其他部分有任何问题(尤其是我如何使用数组),请告诉我。
代码:
import Foundation
import UIKit
class FilterViewController: UIViewController, UITableViewDelegate,UITableViewDataSource, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate,UITextViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate
let properties = ["Diet","Body Type","Looking For","Ethnicity"]
var values:NSArray!
let dietOpts:NSArray = ["Vegan","Plant-based","Vegetarian","Meat Eater"]
let bodytypeOpts:NSArray = ["Muscular","Athletic","Lean","Husky","Chubby","Large","Fat"]
let lookingforOpts:NSArray = ["Friends","Dates","Chat","Networking","Relationship","Right Now"]
let ethnicityOpts:NSArray = ["Hispanic/Latino","Black","White","Middle Eastern","South Asian","Asian","Native American","Pacific Islander"]
var userFilterTable : UITableView!
var cellTypes = [FilterTableViewCell.self,FilterTableViewCell.self,FilterTableViewCell2.self,FilterTableViewCell2.self]
var reminderCells = [FilterTableViewCell(),FilterTableViewCell(),FilterTableViewCell2(),FilterTableViewCell2()]
let cellIDs = ["cellId","cellId","cellId2","cellId2"]
var toolBar : UIToolbar!
var i:Int!
var myUIPicker = UIPickerView()
var multiplePicker = MultiplePicker()
var myValues: NSArray!
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = UIColor.blue
let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
let displayWidth: CGFloat = self.view.frame.width
let displayHeight: CGFloat = self.view.frame.height
userFilterTable = UITableView(frame: CGRect(x: 0, y: barHeight, width: displayWidth, height: displayHeight - barHeight))
userFilterTable.register(FilterTableViewCell.self, forCellReuseIdentifier: "cellId")
userFilterTable.register(FilterTableViewCell2.self, forCellReuseIdentifier: "cellId2")
userFilterTable.backgroundColor = UIColor.clear
userFilterTable.isScrollEnabled = false
userFilterTable.delegate = self
userFilterTable.dataSource = self
userFilterTable.rowHeight = 40
self.userFilterTable.reloadData()
self.view.addSubview(userFilterTable)
@objc func buttonAction(_ sender: UIButton!)
print("Button tapped")
func generalPicker()
myUIPicker.becomeFirstResponder()
self.myUIPicker.delegate = self
self.myUIPicker.dataSource = self
@objc func donePicker()
reminderCells[i].valueTextField.resignFirstResponder()
func doMultiplePicker()
multiplePicker.becomeFirstResponder()
//multiplePicker.delegate = self
//multiplePicker.dataSource = self
reminderCells[i].valueTextField.backgroundColor = UIColor.white
toolBar = UIToolbar()
toolBar.sizeToFit()
@objc func donePicker2()
reminderCells[i].valueTextField.text = multiplePicker.str
reminderCells[i].valueTextField.resignFirstResponder()
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
reminderCells[indexPath.row].valueTextField.becomeFirstResponder()
i = indexPath.row
print(properties[indexPath.item]) //print("Diet")
switch properties[indexPath.item]
case "Diet":
myValues = dietOpts
generalPicker()
case "BodyType":
myValues = bodytypeOpts
generalPicker()
case "Looking For":
myValues = lookingforOpts
generalPicker()
case "Ethnicity":
myValues = bodytypeOpts
doMultiplePicker()
default:
print("Some other character")
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
// retuen no of rows in sections
return properties.count
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
print(indexPath.row)
let cell = tableView.dequeueReusableCell(withIdentifier: cellIDs[indexPath.row], for: indexPath) as! cellTypes[indexPath.row]
switch properties[indexPath.item]
case "Diet","Body Type":
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(donePicker))
toolBar.setItems([doneButton], animated: false)
cell.valueTextField.inputAccessoryView = toolBar
cell.valueTextField.inputView = myUIPicker
case "Looking For","Ethnicity":
multiplePicker.frame = CGRect(x: 0, y: 0, width: 100, height: 300)
multiplePicker.myValues = ethnicityOpts as! [String]
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(donePicker2))
toolBar.setItems([doneButton], animated: false)
cell.valueTextField.inputAccessoryView = toolBar
cell.valueTextField.inputView = multiplePicker
default:
print("default")
cell.backgroundColor = UIColor.clear
cell.property.text = properties[indexPath.item]
cell.isUserInteractionEnabled = true
cell.valueTextField.isUserInteractionEnabled = true
cell.valueTextField.delegate = self
cell.valueTextField.tag = indexPath.item
toolBar = UIToolbar()
toolBar.sizeToFit()
reminderCells[indexPath.item] = cell
return cell
// data method to return the number of column shown in the picker.
func numberOfComponents(in pickerView: UIPickerView) -> Int
return 1
// data method to return the number of row shown in the picker.
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
print(myValues.count)
return myValues.count
// delegate method to return the value shown in the picker
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
return myValues[row] as? String
// delegate method called when the row was selected.
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
reminderCells[i].valueTextField.text = myValues[row] as? String
在 Matt 响应后对 cellForRowAt 进行编辑(这次我在编译时指定类型):
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
switch properties[indexPath.item]
case "Diet","Body Type":
let cell = tableView.dequeueReusableCell(withIdentifier: cellIDs[indexPath.row], for: indexPath) as! FilterTableViewCell
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(donePicker))
toolBar.setItems([doneButton], animated: false)
cell.valueTextField.inputAccessoryView = toolBar
cell.valueTextField.inputView = myUIPicker
cell.backgroundColor = UIColor.clear
cell.property.text = properties[indexPath.item]
cell.isUserInteractionEnabled = true
cell.valueTextField.isUserInteractionEnabled = true
cell.valueTextField.delegate = self
cell.valueTextField.tag = indexPath.item
toolBar = UIToolbar()
toolBar.sizeToFit()
reminderCells[indexPath.item] = cell
return cell
case "Looking For","Ethnicity":
let cell = tableView.dequeueReusableCell(withIdentifier: cellIDs[indexPath.row], for: indexPath) as! FilterTableViewCell2
multiplePicker.frame = CGRect(x: 0, y: 0, width: 100, height: 300)
multiplePicker.myValues = ethnicityOpts as! [String]
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(donePicker2))
toolBar.setItems([doneButton], animated: false)
cell.valueTextField.inputAccessoryView = toolBar
cell.valueTextField.inputView = multiplePicker
cell.backgroundColor = UIColor.clear
cell.property.text = properties[indexPath.item]
cell.isUserInteractionEnabled = true
cell.valueTextField.isUserInteractionEnabled = true
cell.valueTextField.delegate = self
cell.valueTextField.tag = indexPath.item
toolBar = UIToolbar()
toolBar.sizeToFit()
reminderCells[indexPath.item] = cell
return cell
default:
print("default")
【问题讨论】:
是的,我在代码的其他部分遇到了编译错误,它们都涉及对提醒Cells[indexPath.row]的引用......它将它视为基类类型(UITableViewCell)而不是自定义类型(FilterTableViewCell 的)...我什至还不能运行代码。我需要修复 cellforrowat tableview 函数中的“dequereusablecell”语句:let cell = tableView.dequeueReusableCell(withIdentifier: cellIDs[indexPath.row], for: indexPath) as! cellTypes[indexPath.row] ... 具体错误我获取这一行是“数组类型现在用括号括住元素类型” 【参考方案1】:您的dequeue
致电cellForRowAt
提出的这个想法巧妙且看似自然,但却是非法的:
as! cellTypes[indexPath.row]
Swift 不允许您将 (as!
) 转换为直到运行时才指定的类型。它是一种静态语言,而不是动态语言。该类型必须是在编译时指定的 literal 类型。
您只需将 cellForRowAt
实现拆分为两个完全独立的开关案例,一个用于第 0 行和第 1 行,一个用于第 2 和 3 行,并为每个执行不同的 dequeue
,每个声明并指定其自己的本地 cell
变量,并使用强制转换 as! FilterTableViewCell
或 as! FilterTableViewCell2
literally — 并从那里开始,单独,在每种情况下。
您无法像优雅的代码尝试那样统一这两种情况,因为只有在 cell
被键入字面意思为 FilterTableViewCell 或 FilterTableViewCell2 的上下文中,您才能访问属于各自的类型。
至于这个表达方式:
var reminderCells = [FilterTableViewCell(),FilterTableViewCell(),
FilterTableViewCell2(),FilterTableViewCell2()]
...这里的问题是 Swift 只能推断这一定是一个 UITableViewCell 数组,它没有 FilterTableViewCell 属性。
您可以通过定义第三种类型来解决此问题,例如FilterTableViewCellParent,作为FilterTableViewCell和FilterTableViewCell2的公共超类,并赋予it它们可以继承的公共属性; reminderCells
可以是 FilterTableViewCellParent 的数组,您可以通过其中的元素访问属性。 (当然,您也可以将 FilterTableViewCell 设为 FilterTableViewCell2 的超类,或者反之亦然,因此只需使用两种类型。)
但我个人认为你应该放弃“优雅”的尝试,在切换的两种情况下完全分开做所有事情,即使以一些重复为代价。有时,必须像超人穿裤子一样编程;他可能是超人,但他仍然必须一次进入一条腿。
【讨论】:
嘿,马特。明白了。我编辑了我的帖子,并添加了新版本的 cellForRowAt 函数代码。好的,现在我得到的错误涉及我的代码中引用remindCells [i]或remindCells [indexPath.row]的任何地方......错误:“'UITableViewCell'类型的值没有成员'valueTextField'”......基本上swift 不将reminderCells[ ] 识别为自定义单元格类型(FilterTableViewCell 或FilterTableViewCell2,两者都具有“valueTextField”属性并且都是UITableViewCell 的子类)......它将remindCells[] 作为基类类型...... 同样的问题。它看起来很优雅,但你不能那样做。编译器需要在运行时知道这是一个FilterTableViewCell 或FilterTableViewCell2。您不能通过查找数组来“优雅地”做到这一点。这两种类型唯一的共同点是它们是 UITableViewCells,而 UITableViewCell 没有valueTextField
属性。您可以通过检查reminderCells
的推断类型来看到这一点。您可以通过使确实具有valueTextField
的某些第三种单元格类型的FilterTableViewCell和FilterTableViewCell2子类来“解决”它,但您还没有这样做。
hmm 那么当我在same 类型的reminderCells 中制作所有项目时如何...例如,当我制作所有项目FilterTableViewCell() 对象时,我不明白任何错误。为什么只有当reminderCells 混合了FilterTableViewCell() 和FilterTableViewCell2() 对象时,swift 才会发疯?
正如我已经说过的,当你执行后者时,Swift 会自动推断这是一个 UITableViewCell 对象数组,而 没有 具有这些属性。但是,当您执行前者时,Swift 会推断这是一个 FilterTableViewCell 数组,确实具有这些属性。这就是为什么拥有一个确实 具有这些属性的公共超类的解决方案将有助于消除编译器的担忧。基本上你只需要再次阅读我之前的评论。
当然,你也可以将 FilterTableViewCell 设为 FilterTableViewCell2 的超类(反之亦然),从而只使用两种类型。以上是关于在表格视图中放置不同的自定义单元格类型的主要内容,如果未能解决你的问题,请参考以下文章
我想在单个表格视图中使用来自 nib 的两个不同的自定义单元格。两个原型电池具有不同的高度
如何为 MkAnnotation View 自定义视图,就像表格视图的自定义单元格一样?