As férias de Ano Novo já passaram, mas meu desejo de escrever artigos úteis e não muito - não! Hoje falaremos sobre o UITableView , trabalhar com UITableViewDataSource e reutilizar células. Abordaremos como instalar o controlador raiz sem um storyboard, erros ao trabalhar com a tabela, o layout e um cabeçalho grande para a UINavigationBar .
Para quem gosta de piadas engraçadas, gravei um vídeo no YouTube . Bem, aqui tudo será sério. Vamos começar.
Crie um projeto vazio, nomeie o que quiser e vá para o controlador. UIKit tem uma classe UITableViewController . Você pode pesquisar no google muitos tutoriais em que a tabela é mostrada exatamente no contexto desta classe. Mas, para uma melhor compreensão, faremos tudo no UIViewController base.
Na maioria das vezes, quando uma tabela é necessária, o UINavigationController é usado:

Vamos adicionar. No arquivo AppDelegate , a função didFinishLaunchingWithOptions , insira o seguinte código:
let navigationController = UINavigationController.init(rootViewController: ViewController()) self.window = UIWindow.init(frame: UIScreen.main.bounds) self.window?.rootViewController = navigationController self.window?.makeKeyAndVisible()
Um breve programa educacional sobre o motivo: montras e constantes são boas , mas neste tutorial tentaremos ficar sem elas. Esse código ignorará o storyboard (você pode removê-lo) e agrupará o ViewController no UINavigationController .
Seria bom definir um título para o UINavigationBar . Para fazer isso, iremos para a classe ViewController (faremos todo o trabalho adicional aqui) e adicionaremos o seguinte código ao método viewDidLoad :
self.view.backgroundColor = UIColor.white self.navigationItem.title = "Table" self.navigationController?.navigationBar.prefersLargeTitles = true
Agora o título será grande e moderno. Executando o projeto, veremos o seguinte:

Fazendo um TableView
Ações preparatórias estão concluídas, podemos passar para a coisa principal. No controlador, crie uma propriedade UITableView . Escolha um inicializador que tenha um parâmetro Style. Defina qualquer quadro, retornaremos a ele mais tarde. E para o estilo, defina Agrupado .
let tableView = UITableView.init(frame: .zero, style: UITableView.Style.grouped)
Não tenho idéia do porquê desse nome de estilo, mas ele nos permitirá criar uma tabela nativa, como no aplicativo Configurações . Se você precisar de uma tabela não estilizada, use o inicializador apenas com o parâmetro frame.

Layout
Aqui teríamos sido salvos pela constante, mas não procuraremos maneiras fáceis. Vou mostrar o método que eu uso. Obviamente, ele não finge ser canônico, e não é necessário fazê-lo. Declaramos uma função que exporá o quadro às visualizações e o parâmetro assumirá o tamanho do controlador:
private func updateLayout(with size: CGSize) { self.tableView.frame = CGRect.init(origin: .zero, size: size) }
Você precisa chamar a função em dois lugares - no método viewDidLoad e em viewWillTransition :
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) coordinator.animate(alongsideTransition: { (contex) in self.updateLayout(with: size) }, completion: nil) }
Agora a tabela para qualquer orientação será colocada em tela cheia. Você pode adicionar outro processamento ao método updateLayout .
Criando um UITableViewCell
Como o tutorial não trata de células, mas de uma tabela, não vamos nos concentrar na personalização em detalhes. Vamos criar uma classe de célula herdada da base:
class TableViewCell: UITableViewCell { }
Para usar a célula na tabela, você precisa registrar a classe. Para fazer isso, no ViewController , chame o seguinte método na tabela:
self.tableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
Se a classe for clara, o identificador merece atenção. Em 99% dos casos, a regra funcionará:
- " Células da mesma classe devem ter o mesmo identificador "
Existem muito poucas situações em que as células da mesma classe precisam de identificadores diferentes e estão associadas principalmente à renderização e animações de código .
Fonte de dados
Esta é uma propriedade que aponta para o objeto que preencherá a tabela. I.e. implementar o protocolo UITableViewDataSource . São necessários dois métodos:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {} func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}
O primeiro é responsável pelo número de células na seção. Ainda temos uma seção, uma multi-seção (existe essa palavra?) Não consideraremos. O segundo método é obter o objeto da célula. Funciona astuciosamente, não pense que você mordeu um lutador!
Mas primeiro, vamos adicionar uma matriz de linhas que preencherão a tabela.

Agora vamos seguir para a implementação do primeiro método:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch tableView { case self.tableView: return self.data.count default: return 0 } }
Dizemos à tabela que, na seção 0, haverá tantas células quanto elementos na matriz de dados . O segundo método é um pouco mais complicado. É impossível simplesmente inicializar a célula, e tudo por causa do sistema de reutilização de células. Mas não há necessidade de repreender a Apple, na verdade é bom! Para obter o objeto, você precisa chamar o seguinte código:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = self.tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell cell.textLabel?.text = self.data[indexPath.row] return cell }
O método dequeueReusableCell receberá o objeto da célula pelo identificador e nós realizaremos a conversão usando a classe TableViewCell , que deve ser a célula. textLabel é uma propriedade de classe base; nenhuma configuração adicional é necessária.
Resta especificar o dataSource para a tabela e o método viewDidLoad agora deve ter a seguinte aparência:
override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = UIColor.white self.navigationItem.title = "Table" self.navigationController?.navigationBar.prefersLargeTitles = true self.view.addSubview(self.tableView) self.tableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell") self.tableView.dataSource = self self.updateLayout(with: self.view.frame.size) }
Se executarmos o projeto, veremos uma tabela cheia de conteúdo:

Reutilizar
Até agora, tudo parece estar em ordem. Vamos adicionar um disclosureIndicator para uma das células. Este é um acessório que você definitivamente viu no iOS:

Digamos que a tarefa é definir o indicador apenas para a primeira célula. A primeira coisa que vem à mente é adicionar um método if simples ao cellForRowAt :
if indexPath.row == 0 { cell.accessoryType = .disclosureIndicator }
Mas o inimigo está se escondendo! Vamos executar o projeto e rolar a tabela para fora da tela:

O indicador apareceu pela primeira vez, mas aparece aleatoriamente para outras células! Isso é reutilização - é utilizada uma célula mais conveniente na memória (uma ótima explicação para iniciantes, certo?) E configurada de acordo com o método. Às vezes acontece que uma célula com um indicador é puxada. Como resultado, temos um bug desse tipo. O que fazer
Tudo é simples. Existem duas soluções possíveis. O primeiro está no bloco else e implica essencialmente a configuração inequívoca de qualquer célula. O método ficará assim:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = self.tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell cell.textLabel?.text = self.data[indexPath.row] if indexPath.row == 0 { cell.accessoryType = .disclosureIndicator } else { cell.accessoryType = .none } return cell }
Há outra maneira - implementar o método prepareForReuse na célula. Como o nome indica, o método é chamado antes da reutilização. Tudo que você precisa fazer é redefinir a célula para o padrão. O código fica assim:
class TableViewCell: UITableViewCell { override func prepareForReuse() { super.prepareForReuse() self.accessoryType = .none } }
Existem muitas nuances associadas à reutilização. Por exemplo, a tarefa de carregar imagens para uma célula em segundo plano com paginação, altura adaptável, exclusão e inserção animadas. Mas mais sobre isso na segunda parte, se minhas mãos alcançarem)
Para quem procura
Eu tento gravar regularmente um tutorial no meu canal . Você pode encontrar vídeos sobre como desenhar código ou como criar um controlador do Apple Music player , além de um vídeo neste artigo: