我已阅读this question并认为我理解两种方法之间的区别,直到找到一个奇怪的例子:
将表格视图单元格的样式设置为基本,标识符为单元格在Storyboard中,代码如下:
import UIKit
class TableViewController: UITableViewController {
var items: [String]!
override func viewDidLoad() {
super.viewDidLoad()
items = ["first", "second", "third"]
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// either works fine
let cell = tableView.dequeueReusableCellWithIdentifier("Cell")! // let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
}
非常简单,但当我将tableView:cellForRowAtIndexPath:
方法分别更改为1,2,3,4例时:
案例1:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例2:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例3:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例4:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
cell.textLabel?.text = items[indexPath.row]
return cell
}
案例1,2(不起作用):
案例3,4(工作正常):
如何解释?我认为从另一个角度理解这两种方法确实很有帮助,任何意见都是受欢迎的。
答案 0 :(得分:6)
在每种情况下,您为每行出列两个单元格。在案例1和案例2中,您首先调用("Cell", forIndexPath: indexPath)
版本。在这种情况下,表格视图最终以每行两个单元格结束,一个完全重叠并遮挡另一个。您可以在视图检查器中看到这一点,因为您可以修改视角以查看后面的内容:
(我修改了这样的cellForRowAtIndexPath
代码:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath)
cell.textLabel!.text = "First cell for row \(indexPath.row)"
cell = tableView.dequeueReusableCellWithIdentifier("plainCell", forIndexPath: indexPath)
cell.textLabel!.text = "Second cell for row \(indexPath.row)"
print("Cell being returned is \(cell)")
return cell
}
为每个单元格指定不同的文本标签。)在情况3和4中,首先调用("Cell")
版本,表格视图每行只有一个单元格。
为什么不同的行为?如果您创建UITableViewCell
的自定义子类并在故事板中使用它,则可以覆盖各种方法并添加print()
语句以查看正在发生的事情。特别是awakeFromNib
,didMoveToSuperView
和deinit
。发生的情况是,在情况1和2中,创建了第一个单元格(awakeFromNib)并立即将其添加(didMoveToSuperView)到superview,可能是表视图或其子视图之一。在情况3和4中,创建了第一个单元格但未添加到超级视图中。相反,一段时间后,细胞被解除分配(deinit)。
(请注意,如果第二个单元格使用("Cell", forIndexPath: indexPath)
版本出列,它也会立即添加到超级视图中。但是,如果第二个单元格使用("Cell")
版本出列,则仅在cellForRowAtIndexPath
方法返回后添加到superview 。)
因此,关键区别在于("Cell", forIndexPath: indexPath)
版本导致单元格立即添加到表格视图中,甚至在cellForRowAtIndexPath
完成之前。这在您提到的问题/答案中暗示,因为它表明出列单元格的大小正确。
一旦添加到superview,第一个单元格就无法解除分配,因为它的superview仍然有一个强引用。如果单元格使用("Cell")
版本出列,则它们不会添加到超级视图中,因此一旦重新分配cell
变量,就没有对它们的强引用,因此它们将被取消分配。
希望所有这些都有意义。
答案 1 :(得分:1)
dequeueReusableCellWithIdentifier:
并未向您提供保证:单元格可能为nil
,因此您必须检查您的单元格是否为nil
并正确处理,否则您的应用程序将崩溃。
dequeueReusableCellWithIdentifier:forIndexPath:
会为您检查(它总是返回一个单元格)。
对于您的特定情况(Swift),这意味着您可以使用dequeueReusableCellWithIdentifier:forIndexPath:
安全强制解包单元格,而您必须使用if let
语法与第二个单元格。< / p>
示例代码(在Objective-C中,我不使用Swift)
dequeueReusableCellWithIdentifier:forIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" atIndexPath:indexPath];
// Here we know the cell is not nil (....atIndexPath: ensures it)
cell.textLabel.text = items[indexPath.row];
return cell;
}
dequeueReusableCellWithIdentifier:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
// You asked for a cell, but you don't know if it is nil or not
// In Swift, here the cell should be a conditional
// First, check if the cell is nil
if ( cell == nil ) {
// Cell is nil. To avoid crashes, we instantiate an actual cell
// With Swift conditional should be something similar
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
}
// Here you're sure the cell is not nil
// If condicional, you probably will write cell?.textLabel?.text = items[indexPath.row];
cell.textLabel.text = items[indexPath.row];
// Finally, you return the cell which you're 100% sure it's not nil
return cell;
}