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
cellClass
parâmetro substituirá a entrada antiga. Você pode especificarnil
"for"cellClass
se 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?
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":
e duas subclasses -
CellA
eCellB
(cores diferentes para facilitar a visualização de qual delas está sendo usada):Ao subclassificar as células, podemos usar um
cellForRowAt
if/else:Se nos registrarmos
CellA
assim:um layout típico será parecido com este:
Ou, se registrarmos
CellB
assim:obtemos esta saída:
Então, vamos adicionar um botão na parte inferior para alternar o registro:
No lançamento, fica assim:
Se tocarmos no botão "Alternar para...", chamaremos
tableView.register(...)
novamente, alternando entreCellA
e CellB, and call
reloadData()` (código relevante):Então, o que ganhamos ao tocar um botão?
Claramente, as células foram recarregadas, pois "true" mudou para "false"... mas elas ainda estão sendo retiradas da fila
CellA
!Então, vamos rolar algumas linhas para cima:
A visualização de tabela teve que criar 3 novas células em
.dequeueReusableCell
, usandoCellB
a classe conforme esperado... mas começando em "Linha: 14" ela volta a reutilizarCellA
!Se continuarmos rolando:
vemos que a visualização de tabela agora tem 11
CellA
instâncias e 3CellB
instâncias em sua "fila reutilizável"... e não importa qual classe registramos atualmente.Observe também... os documentos afirmam:
Mas, se fizermos o carregamento inicial, executamos este código:
cellForRowAt
será chamado para cada uma das células visíveis e elas ainda serão atualizadas!Se tentarmos rolar, o aplicativo travará
cellForRowAt
com 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.