【问题标题】:WHY ALWAYS initialize 19 cells?为什么总是初始化 19 个单元格?
【发布时间】:2019-10-24 12:37:49
【问题描述】:

我正在学习 Swift 中的 TableView。我的老师给我这个问题:“你有20个单元格,但屏幕显示3个单元格。初始化了多少个单元格,为什么?

这是我的代码。我尝试了 20 多个单元格,它总是初始化 19 个单元格。有人可以向我解释为什么吗?非常感谢你 我在 iPhone 11 ProMax、iOS 13.1 上测试

override func viewDidLoad() {
        super.viewDidLoad()
        myTable.register(UINib(nibName: "ContactCell", bundle: nil), forCellReuseIdentifier: "ContactCell")
        myTable.dataSource = self
        myTable.delegate = self
    }
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20

    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath) as! ContactCell
        cell.textLabel?.text = "Superhero"

        return cell
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return myTable.frame.size.height / 3.0
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print(indexPath.section)
        print(indexPath.row)
    }

此代码在另一个文件中

    class ContactCell: UITableViewCell {

        @IBOutlet weak var cellShow: UIView!
        override func awakeFromNib() {
            super.awakeFromNib()
            print("awakeFromNib")
        }

        override func setSelected(_ selected: Bool, animated: Bool) {
            super.setSelected(selected, animated: animated)

        }  
    }

【问题讨论】:

  • 你怎么知道它正在初始化 19 个单元格,告诉我你在哪里看到它正在这样做。我问这个是因为你不知道它正在这样做,除非你有另一个函数来解析表格视图中的可见单元格或遍历视图树以在调试器中手动控制它们。你怎么知道是 19 岁?
  • 这是因为只有适合您屏幕的单元格被初始化。证明:尝试旋转你的设备——它会更少(假设你从纵向转到横向)。
  • 你可以在func awakeFormNib中看到。我在初始化单元格时打印“awakefromNib”。然后我数。如果我 retunn 100 个单元格,总是打印 19 行“awakefromNib”
  • @Kevin.D 这不是它的工作原理,这与内存中保存了多少个单元格无关。
  • 我认为 LinusGeffarth 是对的。我试过 iphone 8 是 15 格,iphone 11 pro 是 17 格

标签: ios swift uitableview cell


【解决方案1】:

来自 Apple 的文档 (https://developer.apple.com/documentation/uikit/uitableviewdelegate/1614998-tableview):

在它出现在屏幕上之前,表格视图会为表格可见部分中的项目调用此方法。当用户滚动时,表格视图仅在项目在屏幕上移动时调用该方法。每当项目出现在屏幕上时,它都会调用该方法,而不管它之前是否出现在屏幕上。

在布局期间,表格视图将开始填充。在此过程中,正在确定元素的大小。根据单元格的初始高度和表格视图的高度,可以首先为可能出现的行调用heightForRowAt,然后为可能出现的行调用,然后对于出现的行(以及可能刚刚离开屏幕的行)。

print() 语句添加到您的heightForRowAt 函数中,如下所示:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

    // add this line
    print(indexPath)

    return myTable.frame.size.height / 3.0
}

在我的快速测试中,如果 7 个 默认高度 行适合我的表格视图的高度,我会在调试输出中看到:

[0, 0]
[0, 0]
[0, 0]
[0, 1]
[0, 1]
[0, 1]
[0, 2]
[0, 2]
[0, 2]
[0, 3]
[0, 3]
[0, 3]
[0, 4]
[0, 4]
[0, 4]
[0, 5]
[0, 5]
[0, 5]
[0, 6]
[0, 6]
[0, 6]

如您所见,7 个单元格正在初始化,每个单元格调用了 3 次 heightForRowAt

编辑:呃...从零开始...改错了6以更正7


补充说明...

如果设置.estimatedRowHeight,例如:

myTable.estimatedRowHeight = 150

您将看到 更少 个正在初始化的单元格,并且:

myTable.estimatedRowHeight = 10

您将看到 更多 个单元格正在初始化,因为自动布局将使用该值作为初始行高而不是默认行高。

另外,如果您想了解更多关于单元如何重用的信息,请将其添加到您的 didSelectRowAt 处理程序(这只是一个调试/开发功能,可能对生产代码没有用处):

let reusableCells = tableView.value(forKey: "_reusableTableCells")
print(reusableCells as Any)

在我的情况下(没有设置.estimatedRowHeight),我得到:

Optional({
    ContactCell =     (
        "<MyTest.ContactCell: 0x7fd0ccc295b0; baseClass = UITableViewCell; frame = (0 300; 295 100); text = 'Superhero'; clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x6000010fecc0>>",
        "<MyTest.ContactCell: 0x7fd0ccc2a2a0; baseClass = UITableViewCell; frame = (0 400; 295 100); text = 'Superhero'; clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x6000010ff0c0>>",
        "<MyTest.ContactCell: 0x7fd0ccc2ad90; baseClass = UITableViewCell; frame = (0 500; 295 100); text = 'Superhero'; clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x6000010ff4a0>>",
        "<MyTest.ContactCell: 0x7fd0ccc2ba80; baseClass = UITableViewCell; frame = (0 600; 295 100); text = 'Superhero'; clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x6000010ff8a0>>"
    );
})

请注意,“可重用池”中的单元格数量会随着您向上和向下滚动而改变。

另一个编辑:

也值得尝试一下...更改cellForRowAt中的这一行,来自:

cell.textLabel?.text = "Superhero"

到:

cell.textLabel?.text = "Superhero \(indexPath.row)"

现在,在选择行并打印出“reusablecells”时,您可以看到哪个单元格实际上在“池中”。我的结果变为:

Optional({
    ContactCell =     (
        "<MyTest.ContactCell: 0x7fef77608330; baseClass = UITableViewCell; frame = (0 300; 295 100); text = 'Superhero 3'; clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x600003d36d20>>",
        "<MyTest.ContactCell: 0x7fef776091b0; baseClass = UITableViewCell; frame = (0 400; 295 100); text = 'Superhero 4'; clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x600003d37120>>",
        "<MyTest.ContactCell: 0x7fef7760a030; baseClass = UITableViewCell; frame = (0 500; 295 100); text = 'Superhero 5'; clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x600003d37500>>",
        "<MyTest.ContactCell: 0x7fef7760aeb0; baseClass = UITableViewCell; frame = (0 600; 295 100); text = 'Superhero 6'; clipsToBounds = YES; hidden = YES; autoresize = W; layer = <CALayer: 0x600003d37920>>"
    );
})

显示第 4、5、6 和 7 行的单元格(如 text = 属性中所示 --- 同样,从零开始)已初始化并位于“重用池”中。

【讨论】:

  • 谢谢。真的很有帮助
  • 是的,但cellForRowAt 为 iPhone 11 打印了 18 个单元格。我只能假设,在自动布局引擎启动表格视图之前,假设单元格的默认高度(44pt)将导致 812/ 44 = 18 个全细胞。
  • @Desdenova - 请参阅我的编辑以进行澄清和一些其他信息。
猜你喜欢
  • 2013-09-07
  • 1970-01-01
  • 2016-12-18
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 2011-04-28
相关资源
最近更新 更多