【问题标题】:Swift: prevent cells with text in UITableViewCell from being re-initialized after scrollingSwift:防止 UITableViewCell 中带有文本的单元格在滚动后重新初始化
【发布时间】:2015-12-10 16:08:22
【问题描述】:

在我的 iOS 应用程序中,我有一个包含许多单元格的 UITableViewController,其中一些包含一个 UITextField,以便用户可以插入文本。 “问题”是,如果在右侧单元格中输入文本后,用户向下滚动表格然后向上滚动,那么这些单元格将再次初始化并且文本再次空白。有没有办法防止这种情况?这样只有在表格向下滚动时才会初始化单元格...谢谢

【问题讨论】:

  • 你可以让你的UITableViewCell类监听UITextFieldDelegate并处理textFieldEditingDidEnd事件。然后,您可以将您的自定义事件触发到 UITableViewController,并输入文本及其发生的行。然后您可以将这些数据本地存储在UITableViewController 中,并在cellForRowAtIndexPath 方法中分配回。
  • @teamnorge 你应该回答这个问题,因为这是正确的解决方案。
  • 感谢您的建议...恐怕我需要一些代码或教程,因为我对 iOS 编程非常陌生,而且我不知道您在评论中提到的很多事情.. .
  • 我刚刚看到问题还涉及选择器视图和日期选择器视图...它们再次重新初始化:(
  • @rmaddy 我在 Swift 方面的经验较少,但我可以轻松制作 Objective-C 代码示例。但这对OP有用吗:)

标签: ios swift uitableview scroll uiscrollview


【解决方案1】:

参考我对提供的初始代码和代码的评论。

许多开发人员以自然的方式实现UITableViewDatasource 方法。 在cellForRowAtIndexPath 中放入一个条件switch/case 块并依赖于indexPath.row 出列并返回可重用单元格。这没关系,但这也意味着您需要再将一个switch/case 块放入heightForRowAtIndexPath 并可能放入numberOfRowsInSectiondidSelectRowAtIndexPath

如果您需要更改表格视图中单元格的顺序,或者需要在现有单元格或其他内容之间插入新单元格,该怎么办?这导致对现有代码的重大重写。

预定义表格视图(已知行数和顺序)的良好做法是创建一个描述表格视图单元格的对象,并从所有UITableViewDatasourceUITableViewDelegate 方法访问它。该对象还可以用于存储单元格中动态元素的值,并可以相应地更新。

假设它是一个 NSDictionary 的数组(如果我们决定在其中存储数据,则可变一次)。

let cellsData : [[String: String]] = [["type": "TextField","value": ""], ["type": "DatePicker","value": ""], ["type": "TextField","value": "Default Value"]]

在您的UITableViewDatasourceUITableViewDelegate 方法中,您使用indexPath.row 访问您的cellsData 数组,获取NSDictionary 对象解析它,并根据“类型”键的值,将适当的单元格出列。您还必须在任何地方使用 switch/case 块,但您检查来自 cellsData Object 的值而不是 indexPath.row 本身。所以你可以通过更新cellsData 对象来更新你的tableview。你也可以把height放进去等等。

我看到您保留了来自 tableview 中出列的单元格的引用,以便访问它们以检索数据,但如果您将所有实际数据都存储在 cellsData 对象中,则不需要这样做。 如何实现?收听单元格内的事件。创建自定义事件,并从单元格触发它们。

要创建自定义事件,您需要创建自定义delegate 类(参考Implementing a Delegate for a Custom Class

Swift 的概念与 Objective-C 非常相似。首先我们需要创建委托协议类:

protocol BigTextCellDelegate {
    func controller(controller: BigTextCell, textFieldDidEndEditingWithText: String, atIndex: Int)
}

然后你需要在你的自定义单元类中声明一个委托属性。

var delegate: BigTextCellDelegate?

现在你需要在你的tableview类中实现它,首先让你的tableview类在某种程度上符合BigTextCellDelegate协议。

class DetailsNewTaskViewController: UITableViewController, BigTextCellDelegate {
...
}

那我们需要实现BigTextCellDelegate,只要在DetailsNewTaskViewController里面放一个方法即可。

func controller(controller: BigTextCell, textFieldDidEndEditingWithText: String, atIndex: indexRow) {
// @todo: update `cellsData` at indexRow with `textFieldDidEndEditingWithText` text
}

现在您需要将您的自定义单元格列出到UITextFieldDelegate 事件并触发我们的自定义BigTextCellDelegate 事件。 首先你需要让它符合UITextFieldDelegate协议,然后在初始化的时候赋值,你可以在Storyboard中实现。

class BigTextCell: UITableViewCell, UITextFieldDelegate {

@IBOutlet weak var textField: UITextField!
var Int: rowIndex
var delegate: BigTextCellDelegate?

required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
    textField.delegate = self
}

func textFieldDidEndEditing(textField: UITextField) {
    delegate?.controller(self, textFieldDidEndEditingWithText: textField.text, atIndex: rowIndex)
}

}

BigTextCellDelegate 类型的委托属性是可选的,因为我们不能确定它是否为 nil。

现在你需要分配你的tableview来监听BigTextCellDelegate事件。

cellForRowAtIndexPath,输入:

cell = tableView.dequeueReusableCellWithIdentifier(...)
cell.delegate = self
cell.rowIndex = indexPath.row

// @todo: 从 cellsData 数组中获取数据并将其分配回 cell.textField.text

就是这样。

至于 DatePicker 和其他单元格。你可以用同样的方法来做。 从单元格内部分配给 DatePicker 的 valueChanged 并触发新的自定义委托协议上的方法。

datePicker.addTarget(self, action: Selector("dataPickerChanged:"), forControlEvents: UIControlEvents.ValueChanged)

func datePickerChanged(datePicker:UIDatePicker) {
// @todo: trigger event
}

【讨论】:

  • 感谢您详细而准确的回答,非常感谢。我承认有些事情我不明白,但这是由于我缺乏经验。我会试着把你写的付诸实践。所以,我的表格视图控制器类应该实现所有协议的所有方法,对吧?我的表格视图单元格的每种“类型”的协议(带有大文本的单元格,带有小文本的单元格,带有切换器的单元格,带有数据选择器视图的单元格和带有日期选择器视图的单元格)...对?
  • @LoryLory 您不需要为每个自定义单元创建自定义协议,只需为相同类型的单元创建。因此,如果您的大人物、小人物等内部有文本字段,您可以创建一个协议TextCellDelegate 并从您的单元格中触发它。正如您在协议方法中看到的那样,有一个对单元格的引用,使其类型为UITableViewCell 甚至只是id。然后,您不仅可以处理来自 tableview 控制器的行触发事件,还可以处理单元格的类型。但您也可以从cellsData 数组中获取此信息。
  • 很抱歉,我没有成功,现在我的项目充满了错误。我无法创建独特的自定义协议,因为有些单元格有 UITextView,而其他单元格有 UITextField。所以至少我需要两个协议,但我无法将所有协议放在一起......如果你能给我更多的帮助......
  • 您的自定义协议与单元格结构无关。您应该根据结构使单元类本身符合某些协议,提取作为字符串输入的文本(对于 UITextField 和 UITextView 不同)并使用字符串触发您的自定义委托方法controller: textFieldDidEndEditingWithText:。对于带有UITextView 的单元格,您应该使用UITextViewDelegate 并实现textViewDidEndEditing。我认为您还应该将您的协议放入一个单独的文件中。
  • 这取决于你,我不知道你的自定义单元格的结构是什么,如果你使用 Storyboard 那么这当然行不通,你需要实现 initWithCoder 方法。这里重要的是,您需要将textField.delegate = self 放置在init 中的某个位置,这样您就可以订阅您的单元类以列出UITextFieldDelegate 通知,例如:textFieldDidEndEditing,或者您可以通过连接两个点来完成在故事板编辑器中。我建议用谷歌搜索,有很多很好的例子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-08-02
  • 2021-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多