在 UITableViewCell 中使用 属性 观察者设置标签,发生了什么
Setting label with property observer in UITableViewCell, what's happening
在我的自定义 UITableViewCell
中,当设置 属性 时,如果没有标签,它将创建一个标签并设置文本等。如果标签已经存在,则不应这样做任何事物。奇怪的是,当我第一次点击单元格时,它会按原样创建标签。但是当我再次点击单元格时,标签消失了。我在一个干净的项目中重新创建了它,同样的事情正在发生。这种奇怪行为的原因是什么?
CustomTableViewCell:
class TableViewCell: UITableViewCell {
var numberLabel: UILabel!
var views = [String: UILabel]()
var number: Int = 5 {
didSet {
println("AMOUNT: Accessed")
if numberLabel == nil {
println("AMOUNT: Amount is nil")
// Create amount label
numberLabel = UILabel()
if number > 0 { numberLabel.text = String(number) }
numberLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
contentView.addSubview(numberLabel)
views["amount"] = numberLabel
// Amount label constraints
contentView.addConstraint(NSLayoutConstraint(item: numberLabel, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1, constant: 0))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[amount(40)]-8-|", options: nil, metrics: nil, views: views))
} else {
println("AMOUNT: Nothing should be changing or happening \n")
}
}
}
required init(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
}
ViewController:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
var items = [Int]()
var amount = 0
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 }
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as TableViewCell
cell.number = amount
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
amount++
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
编辑:
作为对评论的回应,我的控制台显示:
NUMBER: Accessed
NUMBER: Number is nil
NUMBER: Accessed
NUMBER: Number is nil << label shows "1"
NUMBER: Accessed
NUMBER: Nothing should be changing or happening << label disappears.
NUMBER: Accessed
NUMBER: Nothing should be changing or happening << label shows "1" again
NUMBER: Accessed
NUMBER: Nothing should be changing or happening << label disappears.
NUMBER: Accessed
NUMBER: Nothing should be changing or happening << label shows "1" again
NUMBER: Accessed
NUMBER: Nothing should be changing or happening << label disappears.
我还将背景更改为一种颜色,它就消失了。我想这与 didSet 的 oldValue
有关。当我将值更改为 newValue
时,willSet
也会发生同样的情况。不过,当我使用 "old values" 时什么也没有发生。
我不想对您的设置进行故障排除,而是指出另一件事:通过依赖标签 "being there",您实际上是在使用 UI 元素作为数据源。这违反了 MVC(模型视图控制器)模式,可能会导致各种麻烦,包括您的情况。
我喜欢 table 视图单元格具有强大 属性 的机制,我可以像您在 cellForRowAtIndexPath
中那样优雅地设置它。它产生简洁漂亮的代码。它将数据逻辑与显示分开,在它所属的 view 中进行编码。
所以对于单元子类,我建议不要创建和销毁 UI 元素,因为这不是很有效。相反,在情节提要中设置您需要的所有 UI 元素,然后在 Interface Builder 中连接它们。在 属性 的 setter 中,检查 number
属性 的值并根据需要更改 UI 元素。如果要隐藏标签,将其 hidden
属性 设置为 true
.
理想情况下,您的 table 视图控制器应该知道索引路径处单元格的状态。单元格应该只负责正确显示此状态。
我认为这归因于重复使用相同的两个单元格:
您的 table 只有一行,但您最终从头开始创建了两个单元格,这在初始输出中很明显。它可能会创建两个,因为您在使用第一个单元格时重新加载,从而强制创建第二个单元格。
第一个单元格 (A) 将包含数字 0 和空白标签。
第二个单元格 (B) 将包含数字 1 和其中包含 1 的标签。
当您执行下一个 select 时,table 会重新使用包含空白文本的单元格 A。无论数字如何,这将始终为空白,因为您永远不会为已经有标签的单元格重置数字。
当您执行下一个 select 时,table 重复使用单元格 B,其中的文本带有 1。
此模式会重复出现,因为您总是在使用一个单元格的同时调用重新加载,而重新加载会重用另一个单元格。因此 A,B,A,B.......
解决该问题,每次设置数字时都需要设置标签内容。
在我的自定义 UITableViewCell
中,当设置 属性 时,如果没有标签,它将创建一个标签并设置文本等。如果标签已经存在,则不应这样做任何事物。奇怪的是,当我第一次点击单元格时,它会按原样创建标签。但是当我再次点击单元格时,标签消失了。我在一个干净的项目中重新创建了它,同样的事情正在发生。这种奇怪行为的原因是什么?
CustomTableViewCell:
class TableViewCell: UITableViewCell {
var numberLabel: UILabel!
var views = [String: UILabel]()
var number: Int = 5 {
didSet {
println("AMOUNT: Accessed")
if numberLabel == nil {
println("AMOUNT: Amount is nil")
// Create amount label
numberLabel = UILabel()
if number > 0 { numberLabel.text = String(number) }
numberLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
contentView.addSubview(numberLabel)
views["amount"] = numberLabel
// Amount label constraints
contentView.addConstraint(NSLayoutConstraint(item: numberLabel, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1, constant: 0))
contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[amount(40)]-8-|", options: nil, metrics: nil, views: views))
} else {
println("AMOUNT: Nothing should be changing or happening \n")
}
}
}
required init(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
}
ViewController:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
var items = [Int]()
var amount = 0
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(TableViewCell.self, forCellReuseIdentifier: "cell")
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 }
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as TableViewCell
cell.number = amount
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
amount++
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
}
编辑: 作为对评论的回应,我的控制台显示:
NUMBER: Accessed
NUMBER: Number is nil
NUMBER: Accessed
NUMBER: Number is nil << label shows "1"
NUMBER: Accessed
NUMBER: Nothing should be changing or happening << label disappears.
NUMBER: Accessed
NUMBER: Nothing should be changing or happening << label shows "1" again
NUMBER: Accessed
NUMBER: Nothing should be changing or happening << label disappears.
NUMBER: Accessed
NUMBER: Nothing should be changing or happening << label shows "1" again
NUMBER: Accessed
NUMBER: Nothing should be changing or happening << label disappears.
我还将背景更改为一种颜色,它就消失了。我想这与 didSet 的 oldValue
有关。当我将值更改为 newValue
时,willSet
也会发生同样的情况。不过,当我使用 "old values" 时什么也没有发生。
我不想对您的设置进行故障排除,而是指出另一件事:通过依赖标签 "being there",您实际上是在使用 UI 元素作为数据源。这违反了 MVC(模型视图控制器)模式,可能会导致各种麻烦,包括您的情况。
我喜欢 table 视图单元格具有强大 属性 的机制,我可以像您在 cellForRowAtIndexPath
中那样优雅地设置它。它产生简洁漂亮的代码。它将数据逻辑与显示分开,在它所属的 view 中进行编码。
所以对于单元子类,我建议不要创建和销毁 UI 元素,因为这不是很有效。相反,在情节提要中设置您需要的所有 UI 元素,然后在 Interface Builder 中连接它们。在 属性 的 setter 中,检查 number
属性 的值并根据需要更改 UI 元素。如果要隐藏标签,将其 hidden
属性 设置为 true
.
理想情况下,您的 table 视图控制器应该知道索引路径处单元格的状态。单元格应该只负责正确显示此状态。
我认为这归因于重复使用相同的两个单元格:
您的 table 只有一行,但您最终从头开始创建了两个单元格,这在初始输出中很明显。它可能会创建两个,因为您在使用第一个单元格时重新加载,从而强制创建第二个单元格。
第一个单元格 (A) 将包含数字 0 和空白标签。 第二个单元格 (B) 将包含数字 1 和其中包含 1 的标签。
当您执行下一个 select 时,table 会重新使用包含空白文本的单元格 A。无论数字如何,这将始终为空白,因为您永远不会为已经有标签的单元格重置数字。
当您执行下一个 select 时,table 重复使用单元格 B,其中的文本带有 1。
此模式会重复出现,因为您总是在使用一个单元格的同时调用重新加载,而重新加载会重用另一个单元格。因此 A,B,A,B.......
解决该问题,每次设置数字时都需要设置标签内容。