AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 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

A documentação do iOS da Apple está incorreta sobre a substituição de classes registradas anteriormente com o mesmo identificador de reutilização pela nova cellClass?

  • 772

A documentação da Apple diz que:

Se você registrou anteriormente uma classe ou arquivo nib com o mesmo identificador de reutilização, a classe especificada no cellClassparâmetro substituirá a entrada antiga. Você pode especificar nil"for" cellClassse desejar cancelar o registro da classe do identificador de reutilização especificado.

Até onde sei, isso não parece estar correto.

O código de exemplo simples abaixo demonstra o problema. Basicamente, tenho um controle deslizante que altera o valor do preenchimento. Quando o preenchimento muda, ele deve registrar novamente (substituir a entrada antiga) a classe com o identificador de reutilização e recarregar a tabela para mostrar o novo preenchimento:

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")
    }
}

No entanto, esse não parece ser o caso. Ele continua usando o preenchimento anterior e a entrada antiga não é substituída.

Estou entendendo errado o que a documentação da Apple diz?

  • 1 1 respostas
  • 80 Views

1 respostas

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

    Embora o código de exemplo publicado seja uma abordagem ruim para começar, parece que a documentação não está correta. Ou não está nada claro o que deveria acontecer.

    Vejamos este exemplo (NÃO usando SnapKit)...

    Criaremos uma classe de célula "Base":

    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")
        }
    }
    

    e duas subclasses - CellAe CellB(cores diferentes para facilitar a visualização de qual delas está sendo usada):

    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")
        }
    }
    

    Ao subclassificar as células, podemos usar um cellForRowAtif/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
    }
    

    Se nos registrarmos CellAassim:

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

    um layout típico será parecido com este:

    um

    Ou, se registrarmos CellBassim:

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

    obtemos esta saída:

    b

    Então, vamos adicionar um botão na parte inferior para alternar o registro:

    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
        }
    }
    

    No lançamento, fica assim:

    c

    Se tocarmos no botão "Alternar para...", chamaremos tableView.register(...)novamente, alternando entre CellAe CellB , and call reloadData()` (código relevante):

    @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: [])
    }
    

    Então, o que ganhamos ao tocar um botão?

    d

    Claramente, as células foram recarregadas, pois "true" mudou para "false"... mas elas ainda estão sendo retiradas da filaCellA !

    Então, vamos rolar algumas linhas para cima:

    e

    A visualização de tabela teve que criar 3 novas células em .dequeueReusableCell, usando CellBa classe conforme esperado... mas começando em "Linha: 14" ela volta a reutilizar CellA !

    Se continuarmos rolando:

    f

    vemos que a visualização de tabela agora tem 11 CellAinstâncias e 3 CellBinstâncias em sua "fila reutilizável"... e não importa qual classe registramos atualmente.


    Observe também... os documentos afirmam:

    Você pode especificar nil para cellClass se quiser cancelar o registro da classe do identificador de reutilização especificado.

    Mas, se fizermos o carregamento inicial, executamos este código:

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

    cellForRowAtserá chamado para cada uma das células visíveis e elas ainda serão atualizadas!

    Se tentarmos rolar, o aplicativo travará cellForRowAtcom a mensagem "Erro fatal: encontrado inesperadamente nulo ao desembrulhar um valor opcional".

    A falha na rolagem faz sentido, embora o recarregamento de células já enfileiradas não faça.

    • 1

relate perguntas

  • Adicionar número de série para atividade de cópia ao blob

  • A fonte dinâmica do empacotador duplica artefatos

  • Selecione linhas por grupo com 1s consecutivos

  • Lista de chamada de API de gráfico subscritoSkus estados Privilégios insuficientes enquanto os privilégios são concedidos

  • Função para criar DFs separados com base no valor da coluna

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

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

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve