AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 79575976
Accepted
sudoExclamationExclamation
sudoExclamationExclamation
Asked: 2025-04-16 04:13:43 +0800 CST2025-04-16 04:13:43 +0800 CST 2025-04-16 04:13:43 +0800 CST

苹果的 iOS 文档中关于先前注册的具有相同重用标识符的类被新的 cellClass 替换的说法是否不正确?

  • 772

苹果的文档指出:

如果您之前注册了具有相同重用标识符的类或 nib 文件,则您在参数中指定的类将替换旧条目。如果您想从指定的重用标识符中取消注册该类,cellClass请指定nil。cellClass

据我所知,这似乎不正确。

下面简单的示例代码演示了这个问题。基本上,我有一个可以改变填充值的滑块。当填充值发生变化时,它应该用重用标识符重新注册(替换旧条目)该类,并重新加载表格以显示新的填充值:

import UIKit
import SnapKit

extension String {
    static let kPadding = Self("padding")
    static let cellId = Self("cell")
}

class ViewController: UIViewController, UITableViewDataSource {
    
    let tableView = UITableView(frame: .zero, style: .plain)

    override func viewDidLoad() {
        super.viewDidLoad()
        
        registerCell()
        tableView.dataSource = self
        
        view.addSubview(tableView)
        tableView.snp.makeConstraints { make in
            make.horizontalEdges.top.equalToSuperview()
        }
        
        let slider = UISlider()
        slider.isContinuous = false
        slider.minimumValue = 0
        slider.maximumValue = 100
        slider.value = UserDefaults.standard.float(forKey: .kPadding)
        slider.addTarget(self, action: #selector(sliderChanged(slider:)), for: .valueChanged)
        
        view.addSubview(slider)
        slider.snp.makeConstraints { make in
            let padding = 15.0
            make.horizontalEdges.bottom.equalTo(view.safeAreaLayoutGuide).inset(padding)
            make.top.equalTo(tableView.snp.bottom).offset(padding)
        }
        
    }
    
    @objc func sliderChanged(slider : UISlider) {
        print("sliderChanged: \(slider.value)")
        UserDefaults.standard.set(slider.value, forKey: .kPadding)
        registerCell()
        tableView.reloadData()
    }
    
    func registerCell(){
        tableView.register(Cell.self, forCellReuseIdentifier: .cellId)
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        100
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: .cellId) as! Cell
        
        cell.label.text = "Hello \(indexPath.row)"
        
        return cell
    }

}

class Cell: UITableViewCell {
    
    let label = UILabel()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        label.font = UIFont.systemFont(ofSize: 34, weight: .bold)
        
        contentView.addSubview(label)
        label.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(UserDefaults.standard.float(forKey: .kPadding))
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

然而,事实似乎并非如此。它继续使用之前的填充,并且旧条目不会被替换。

我是否误解了 Apple 文档的内容?

  • 1 1 个回答
  • 80 Views

1 个回答

  • Voted
  1. Best Answer
    DonMag
    2025-04-17T00:32:09+08:002025-04-17T00:32:09+08:00

    虽然发布的示例代码一开始就很糟糕,但文档似乎也不正确。或者说,它根本就不清楚应该发生什么。

    让我们看一下这个例子(不使用 SnapKit)...

    我们将创建一个“基础”细胞类:

    class CellBase: UITableViewCell {
        
        let label = UILabel()
        
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            label.font = UIFont.systemFont(ofSize: 20, weight: .regular)
            
            label.translatesAutoresizingMaskIntoConstraints = false
            contentView.addSubview(label)
            
            let g = contentView.layoutMarginsGuide
    
            let h = label.heightAnchor.constraint(equalToConstant: 44.0)
            h.priority = .required - 1
            
            NSLayoutConstraint.activate([
                label.topAnchor.constraint(equalTo: g.topAnchor),
                label.leadingAnchor.constraint(equalTo: g.leadingAnchor),
                label.trailingAnchor.constraint(equalTo: g.trailingAnchor),
                label.bottomAnchor.constraint(equalTo: g.bottomAnchor),
                h,
            ])
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    

    和两个子类 -CellA和CellB(不同的颜色可以很容易地看出哪一个正在被使用):

    class CellA: CellBase {
        
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            
            label.backgroundColor = .yellow
            contentView.backgroundColor = .systemYellow
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
    class CellB: CellBase {
        
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            
            label.backgroundColor = .cyan
            contentView.backgroundColor = .systemBlue
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    

    通过对单元格进行子类化,我们可以使用cellForRowAt不带 if/else 的情况:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cellBase") as! CellBase
        cell.label.text = "Row: \(indexPath.row) - use Cell A: \(useCellA)"
        return cell
    }
    

    如果我们CellA这样注册:

    tableView.register(CellA.self, forCellReuseIdentifier: "cellBase")
    

    典型的布局如下:

    一个

    或者,如果我们CellB像这样注册:

    tableView.register(CellB.self, forCellReuseIdentifier: "cellBase")
    

    我们得到这个输出:

    b

    因此,让我们在底部添加一个按钮来切换注册:

    class ChangeCellRegVC: UIViewController, UITableViewDataSource {
        
        let tableView = UITableView(frame: .zero, style: .plain)
        let btn = UIButton()
        
        var useCellA: Bool = true
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            view.backgroundColor = .systemBackground
        
            btn.setTitle("Switch to Cell B", for: [])
            btn.backgroundColor = .systemRed
            btn.setTitleColor(.white, for: .normal)
            btn.setTitleColor(.lightGray, for: .highlighted)
            btn.addTarget(self, action: #selector(btnTap(_:)), for: .touchUpInside)
            
            tableView.translatesAutoresizingMaskIntoConstraints = false
            btn.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(tableView)
            view.addSubview(btn)
    
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                tableView.topAnchor.constraint(equalTo: g.topAnchor),
                tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
                tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
    
                btn.topAnchor.constraint(equalTo: tableView.bottomAnchor, constant: 20.0),
                btn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
                btn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
                btn.bottomAnchor.constraint(equalTo: g.bottomAnchor),
            ])
    
            tableView.register(CellA.self, forCellReuseIdentifier: "cellBase")
            tableView.dataSource = self
        }
    
        @objc func btnTap(_ sender: UIButton) {
            useCellA.toggle()
            if useCellA {
                print("register CellA")
                tableView.register(CellA.self, forCellReuseIdentifier: "cellBase")
            } else {
                print("register CellB")
                tableView.register(CellB.self, forCellReuseIdentifier: "cellBase")
            }
    
            tableView.reloadData()
    
            // update button title
            btn.setTitle("Switch to Cell \(useCellA ? "B" : "A")", for: [])
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            100
        }
        
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "cellBase") as! CellBase
            cell.label.text = "Row: \(indexPath.row) - use Cell A: \(useCellA)"
            return cell
        }
    }
    

    启动时,它看起来像这样:

    c

    如果我们点击“切换到...”按钮,我们将tableView.register(...)再次调用,在CellA和 CellB , and call reloadData() 之间切换(相关代码):

    @objc func btnTap(_ sender: UIButton) {
        useCellA.toggle()
        if useCellA {
            print("register CellA")
            tableView.register(CellA.self, forCellReuseIdentifier: "cellBase")
        } else {
            print("register CellB")
            tableView.register(CellB.self, forCellReuseIdentifier: "cellBase")
        }
        
        tableView.reloadData()
        
        // update button title
        btn.setTitle("Switch to Cell \(useCellA ? "B" : "A")", for: [])
    }
    

    那么,点击按钮我们能得到什么呢?

    d

    显然,由于“真”已变为“假”,因此单元格已重新加载......但它们仍在出队CellA!

    那么,让我们向上滚动几行:

    e

    表格视图必须在 上创建 3 个新单元格.dequeueReusableCell,并按预期使用CellB类...但从“行:14”开始,它又回到了重新使用 CellA!

    如果我们继续滚动:

    f

    我们看到表格视图现在有 11 个CellA实例,CellB其“可重用队列”中有 3 个实例......并且我们当前注册了哪个类并不重要。


    另请注意...文档指出:

    如果您想从指定的重用标识符中取消注册该类,则可以为 cellClass 指定 nil。

    但是,如果我们进行初始加载,则执行以下代码:

    tableView.register(nil as UINib?, forCellReuseIdentifier: "cellBase")
    tableView.reloadData()
    

    cellForRowAt将被每个可见单元格调用并且它们仍将更新!

    如果我们尝试滚动,应用程序将崩溃cellForRowAt并显示“致命错误:在解开可选值时意外发现 nil”

    滚动时崩溃是有道理的,尽管重新加载已经排队的单元格却没有道理。

    • 1

相关问题

  • 将复制活动的序列号添加到 Blob

  • Packer 动态源重复工件

  • 选择每组连续 1 的行

  • 图形 API 调用列表 subscribedSkus 状态权限不足,但已授予权限

  • 根据列值创建单独的 DF 的函数

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    重新格式化数字,在固定位置插入分隔符

    • 6 个回答
  • Marko Smith

    为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会?

    • 2 个回答
  • Marko Smith

    VScode 自动卸载扩展的问题(Material 主题)

    • 2 个回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Martin Hope
    Fantastic Mr Fox msvc std::vector 实现中仅不接受可复制类型 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant 使用 chrono 查找下一个工作日 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor 构造函数的成员初始化程序可以包含另一个成员的初始化吗? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský 为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul C++20 是否进行了更改,允许从已知绑定数组“type(&)[N]”转换为未知绑定数组“type(&)[]”? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann 为什么 {2,3,10} 和 {x,3,10} (x=2) 的顺序不同? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve