从笔尖初始化自定义 UITableViewCell 没有 dequeueReusableCellWithIdentifier

Posted

技术标签:

【中文标题】从笔尖初始化自定义 UITableViewCell 没有 dequeueReusableCellWithIdentifier【英文标题】:Init custom UITableViewCell from nib without dequeueReusableCellWithIdentifier 【发布时间】:2016-07-08 14:33:24 【问题描述】:

SWIFT

我需要制作一个单元格数组。我有几个带有 nib 文件的自定义单元格类(继承自 UITableViewCell)。

如何在不在 tableview 中注册 nib 并执行 dequeueReusableCellWithIdentifier 的情况下初始化单元格? 我是这样做的,但不认为它会起作用:

var labelCell = CustomCellClass.initialize()

【问题讨论】:

为什么“没有注册 nib”以及为什么“没有 dequeueReusableCellWithIdentifier”? 这个问题没关系。但是,如果您只是出于兴趣而想知道-制作一张静态表格。我需要它从放置在我的单元格中的文本字段、分段控件和切换器中获取值。 你会在NSBundle上使用loadNibNamed:owner:options:方法 静态的tableview很重要。 只有dans回答这个问题?)) 【参考方案1】:

我从其他地方 cmets 的讨论中推断,您不想让单元格出列和重用的原因是您无法跟踪单元格中捕获的用户输入。

最重要的是,您确实应该允许单元格出列和重用,并适当地处理它。如果您在重用单元时遇到问题,可以通过将“模型”(即您的数据)与“视图”(即 UIKit 控件)分开来解决。这是model-view-controller pattern 的精神,但在任何具有关注点分离的模式(例如,MVVP、MVP 等)中都是如此。

关键是当单元格中的值发生变化时,您的单元格应该立即告诉视图控制器,以便视图控制器可以立即更新模型。然后,当视图控制器稍后需要对与特定行关联的值执行某些操作时,它不会从单元格中检索它,而是从它自己的模型中检索它。

因此,我可能会为单元格定义一个协议,以通知表格视图其文本字段已更改:

protocol CustomCellDelegate: class 
    func cell(_ cell: CustomCell, didUpdateTextField textField: UITextField)

然后我会定义一个调用该委托的单元类:

class CustomCell: UITableViewCell 
    weak var delegate: CustomCellDelegate?

    @IBOutlet weak var customTextField: UITextField!          // hook up outlet to this property in IB

    @IBAction func didChangeValue(_ sender: UITextField)       // hook up "editing changed" action for the text field to this method in IB
        delegate?.cell(self, didUpdateTextField: sender)
    

现在,视图控制器将:

向有问题的 NIB 注册重用标识符; 在cellForRowAt 中,填充文本字段并将自己指定为该单元格的代表;和 如果用户更改任何内容,请处理 didUpdateTextField 方法以更新模型。

因此,类似于:

class ViewController: UITableViewController 

    var values = ["One", "Two", "Three"]  // some initial values

    private let cellIdentifier = "CustomCell"

    override func viewDidLoad() 
        super.viewDidLoad()

        // if you’re using NIBs, you register them. 
        // obviously if using prototype cells in your storyboard, this isn’t necessary.

        tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: cellIdentifier) // or use cell prototype with storyboard identifer specified
    


// MARK: - UITableViewDataSource

extension ViewController 

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return values.count
    

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! CustomCell

        // populate cell and specify delegate

        cell.delegate = self
        cell.customTextField.text = values[indexPath.row]

        return cell
    


// MARK: - CustomCellDelegate

extension ViewController: CustomCellDelegate 
    func cell(_ cell: CustomCell, didUpdateTextField textField: UITextField) 
        // when the cell tells us that its text field's value changed, update our own model

        if let indexPath = tableView.indexPath(for: cell), let string = textField.text 
            values[indexPath.row] = string
        
    

许多人可能倾向于通过将文本字段的IBAction 直接挂钩到视图控制器方法来进一步简化此操作。这行得通,并且消除了对这个协议的需要,但问题是你需要弄清楚这个特定的 UIKit 控件与哪一行相关联。常见的技巧是向上导航视图层次结构以识别适当的单元格(例如,通常文本字段将位于单元格内的内容视图中,因此您抓住textField.superview.superview as! UITableViewCell),但这对我来说感觉有点脆弱。

但不管这些小细节如何,希望这能说明更广泛的模式。与其试图让单元格跟踪用户输入,不如让单元格(“视图”)立即更新控制器的任何数据更改,然后视图控制器立即更新模型,您不再需要担心ios 采用的单元重用优化。

对于 Swift 2 版本,请参阅 previous revision of this answer。

【讨论】:

【参考方案2】:

静态表的想法是它完全在 IB 中定义。一种解决方案是将这些单元格从其孤立的 NIB 复制粘贴到包含表格的单元格中。

更好的解决方案是使表格动态化,并让动态代码返回静态数量的部分和行。编写其余的逻辑,就好像它是动态的一样(例如,注册所有的 nib,并初始化一个单元格标识符数组,您希望它们在表中以何种方式组织,使用该数组出列)。

【讨论】:

问题是,当我想从单元格中获取一些值时,这些值已出列 - 它们为零。只有当所有单元格都显示在屏幕上时,您的解决方案才有效。 您能进一步解释一下吗?考虑从单元格 获取值是一个常见的错误。只有一个一个单元格。这些值保存在模型(也称为数据源)中。 如果我能找到如何从模型中获取数据(不是从单元格中获取数据,因为当单元格超出屏幕边界时会变为空),那就太好了。我做了下一步let cell = self.tableView.cellForRowAtIndexPath(NSIndexPath.init(forRow: 0, inSection: NKVPhoneField)) as? NKVTextFieldTableVCell,然后从单元格print(cell?.registrationTextField.text)获取价值 我明白了。如果单元格包含文本字段,则必须使视图控制器成为该文本字段的委托,并且在编辑文本字段时,该委托必须在模型中记录该字段中的文本。在此处查看答案(答案中的“替代”部分)***.com/a/27743391/294949 :-) 你只会值给单元格。一分钟,忘记表格视图。制作一个代表用户将看到的对象的数组。控制器的工作是从该数组(并且仅该数组)中提供和获取值。另一个工作是重新加载表格视图并配置单元格(当 cellForRowAtIndexPath 询问时)以匹配该数组中的内容。这在 tableview 编程中是一个非常重要的思想,在一般的 MVC 中也是如此。【参考方案3】:

另一种解决方法:

1) 视图控制器中的 UITextField 委托。将值存储在数组中

var textFieldValues = ["", "", "", "", "", "", "", "", "", ""] // with count of text fields in table. Init in top of ViewController.

//MARK:TextField Delegate

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool 
    if string == " "
    
        return false
    

    let indexPathForCellWhereTextFieldIs = self.tableView.indexPathForCell(textField.superview?.superview as! UITableViewCell)
    textFieldValues[(indexPathForCellWhereTextFieldIs?.section)!] = (textField.text! as NSString).stringByReplacingCharactersInRange(range, withString: string) as NSString as String

    return true

2) 在 indexPathForRow

    cell.registrationTextField.text = textFieldValues[indexPath.section]
    cell.registrationTextField.delegate = self

3) 从数组中获取数据

func tapOnRegisterButton(sender:UIButton)

    debugPrint(textFieldValues)

【讨论】:

以上是关于从笔尖初始化自定义 UITableViewCell 没有 dequeueReusableCellWithIdentifier的主要内容,如果未能解决你的问题,请参考以下文章

来自笔尖的自定义 UiTableViewCell 没有重用?

来自 NIB 的可扩展自定义 UITableViewCell

笔尖泄漏的自定义单元格

使用笔尖的 UITableViewCell 背景颜色

来自 NSString 的 UITableViewCell 笔尖

从笔尖创建 UITableViewCell,动态单元格高度