Criando sections personalizadas dinamicamente

Criação de um app básico abordando conceitos de sections em TableView’s

--

Sabemos muito bem que TableView’s estão presentes em quase todos aplicativos iOS atualmente, sendo um componente extremamente versátil e útil. Mas quantas vezes vemos TableView’s com mais de uma section? Ou com sections que possuem suas próprias views e são criadas conforme a necessidade do usuário? Eu, particularmente, me interessei em fazer um app com essas características e ao aprender mais sobre isso, resolvi escrever este artigo para mostrar como isso pode ser poderoso e, mesmo assim, ser realizado sem muito esforço. Bom, chega de papo, vamos codar!

A proposta deste artigo é criar um aplicativo para adicionarmos diferentes ligas nacionais de futebol e seus respectivos times. Ao final deste tutorial devemos ter um app parecido com isso:

Para começar, baixe o repositório base que disponibilizei neste link, nele está criado um projeto simples no Xcode, com apenas um UITableViewController no Storyboard, um arquivo LigasTableViewController.swift e os grupos Model e View que utilizaremos ao decorrer do artigo (além dos arquivos padrões de um projeto iOS, é claro).

Nosso arquivo LigasTableViewController.swift deve estar mais ou menos assim:

Por enquanto temos zero sections e zero linhas, mas logo mudaremos isso

Seguindo em frente, precisamos definir variáveis para armazenar nossas ligas e os times. Com isso em mente, crie um novo arquivo Swift dentro do grupo Model e o nomeie como Liga:

Para nossa classe Liga, vamos trocar o import Foundation para import UIKit, para podermos usar uma UIImage. Nessa classe precisaremos armazenar um nome para a liga, uma imagem do país da liga e os times pertencentes à mesma, que para facilitar, será um vetor de String. Também é interessante criar métodos para adicionar e/ou remover times na liga, deixando nosso arquivo assim:

Foi criado um construtor que recebe como parâmetro um nome e uma imagem. Note que a variável times é inicializada como um vetor vazio.

Tomei a liberdade de criar um enum para representar as bandeiras que estão disponíveis nos assets do app, para facilitar a criação da ImageView. O enum foi adicionado antes da definição da classe Liga:

Caso o país escolhido não possua sua imagem nos assets do app, uma imagem padrão será utilizada

Em nosso LigasTableViewController precisamos agora criar um vetor para armazenar nossas ligas:

Note que os métodos numberOfSections(...) e tableView(... numberOfRowsInSection ...) agora retornam respectivamente a quantidade de ligas (quantidade de sections) e a quantidade de times em cada liga (quantidade de linhas em cada section).

Por enquanto nossa TableView está completamente vazia, já que os dados dependem de nossa variável ligas, que também está vazia. Está na hora de disponibilizarmos uma forma para o usuário adicionar sections em nosso app, então, basta criarmos um botão.

Primeiramente, para fins de interface do usuário, vamos colocar nosso TableViewController em um NavigationController, podendo assim, adicionar um botão na barra de navegação. Para fazer isso, selecione nosso TableViewController no Storyboard e com ele selecionado vá em Editor > Embed In > Navigation Controller.

Agora podemos colocar um botão no canto superior direito do nosso navigation item, e deixá-lo com uma aparência coerente para o nosso exemplo:

Note que também defini um título para o navigation item

Vamos linkar uma action desse botão com o nosso código:

Atenção para que a conexão sendo feita seja uma Action e não uma Outlet

O usuário precisará de uma forma para preencher o nome da liga, e para deixar o app mais simples, vamos criar um AlertController com um TextField para que isso seja possível. Nosso próximo passo é criar uma classe para definirmos um AlertController personalizado, então vamos criar o AddLigaAlertController.swift no grupo Controller:

Atenção para que seja uma subclasse de UIAlertController

Precisamos definir o que precisaremos em nosso novo alerta, como um TextField e as ações que serão apresentadas:

Agora no nosso LigasTableViewController precisamos declarar uma referência para nosso novo alerta e instanciá-lo no viewDidLoad:

Olhando nosso AddLigaAlertController, podemos ver que a ação a ser realizada quando o botão “Add” do alerta é pressionado ainda não faz nada, e para corrigir isso podemos criar um protocolo chamado LigasTableViewControllerDelegate que servirá para que a ação realizada no nosso alerta possa alterar a variável ligas da outra classe. Vamos começar:

Definição do protocolo no topo do arquivo LigasTableViewController.swift

Defini três funções para o protocolo, duas para serem chamadas quando uma liga ou um time deva ser adicionado(a) e outra para controlarmos quando um alerta pode ser apresentado para o usuário.

Com o protocolo criado, vamos conformar nosso LigasTableViewController à ele, para podermos implementar as funções do mesmo. Vamos adicionar uma extension no fim do arquivo para isso:

O passo seguinte é declarar uma variável delegate em nossa classe AddLigaAlertController e utilizá-la para adicionar uma liga na ação de “Add” do nosso alerta, deixando a classe assim:

Para que uma liga seja realmente adicionada, vamos completar a função addLiga(nome: String) no LigasTableViewController.swift:

Agora na nossa @IBAction func criada para o botão devemos então apresentar nosso addLigaAlertController para o usuário poder interagir com o app:

E para que algo seja realmente mostrado precisamos implementar nosso método mostrar(alerta: UIAlertController?) do delegate:

Obs.: A verificação se a variável section recebida por parâmetro não é nula será utilizada posteriormente, quando criarmos nosso alerta para adicionar um time.

Por fim, temos que dizer que o delegate do addLigaAlertController é a nossa classe LigasTableViewController:

Pronto! Nosso alerta já está funcionando. Agora podemos começar a criar o visual da nossa section, para apresentarmos o nome da liga, a imagem de seu país e um botão para adicionar times. O próximo passo então é criar nossa view personalizada, o LigaView.swift no grupo View:

Garanta que LigaView herde de UIView

Neste arquivo precisamos definir o visual que nossa section terá, resolvi colocar uma imagem no canto esquerdo, uma label logo ao lado e um botão para criarmos os times. Foram criadas constraints para esses elementos e já começamos declarando o delegate que também utilizaremos:

Para que nossa view customizada seja visível, precisamos sobrescrever dois métodos do UITableViewDelegate no nosso LigasTableViewController:

Para que o app ficasse mais bonito, deixei a cor de fundo da section branca, a da table view — via Storyboard — com um verde (#009051), a da navigation controller também branca e o tint do botão para adicionar ligas com o mesmo verde do fundo da table view. Agora nosso app está funcionando e se parece com isso:

Quando a bandeira não está disponível nos assets, aparece uma padrão

Dica: para tirar essas linhas brancas, podemos definir um footer para a table view. Basta adicionar o código tableView.tableFooterView = UIView(frame: CGRect.zero) no viewDidLoad do LigasTableViewController. O app ficará assim após a alteração:

Ótimo! Agora nosso aplicativo já consegue criar as ligas nacionais que tanto queríamos. Para finalizar o tutorial (ufa!), precisamos apenas fazer com que os botões das sections consigam adicionar times em sua respectiva liga.

Uma forma de fazer isso é criando mais um alerta personalizado, que no caso será o AddTimeAlertController:

E podemos a preencher dessa forma:

Essa classe é muito parecida com a do nosso alerta anterior, porém ela possui uma variável do tipo Liga? para sabermos onde colocar o time adicionado.

Em nosso LigasTableViewController vamos declarar e instanciar uma variável para nosso novo alerta:

E para podermos seguir em frente, vamos terminar de implementar nosso método addTime(…):

Agora podemos chamar esse método na ação de “Add” do nosso novo alerta:

Agora percebe que algo está faltando? Pois é, nosso botão de adicionar um time ainda não faz nada, então vamos adicionar um target a ele mexendo na nossa classe LigaView:

Note que como section estamos passando a tag do botão, que é definida como sendo igual à section equivalente.

Agora lembra-se daquela verificação se a section é nula no método mostrar(…) do nosso LigasTableViewController? Então, vamos terminar a implementação:

Em resumo, se a section não for nula sabemos que se trata do addTimeAlertController, precisando assim informar qual liga ele pertence.

Para que o botão de adicionar um time funcione, falta definir o delegate da nossa classe LigaView:

E por fim, para atualizar a inserção de elementos na tableview precisamos completar o método addTime(…) com um self.tableView.reloadData():

Porém, se você rodar o projeto agora, verá que o app vai crashar ao tentar adicionar um time. Isso ocorre pois não definimos como nossas células irão se comportar, então vamos ajeitar isso. Adicione ao final da classe LigasTableViewController a sobrescrita do método tableView(... , cellForRowAt ...) e crie alguma célula simples, como visto abaixo:

Célula simples, com apenas uma label. O texto dessa label é setado baseado na section e na linha, pegando a liga e o time correto

Pronto! Agora nosso app está — finalmente — finalizado.

Se quiser baixar o projeto completo, este é o repositório:

Para deixar o artigo mais simples (ou menos complexo) não utilizei CoreData para persistir os dados, então cada vez que o aplicativo é fechado e aberto novamente, as ligas e times não existem mais. Mas basta adaptar este tutorial que é possível misturar as coisas e armazenar as informações adicionadas pelo usuário.

Espero que o tutorial tenha ficado entendível e que possa ajudar.

Até a próxima! 👊🏻

--

--

Apple Developer Academy | Mackenzie
Apple Developer Academy | Mackenzie

Published in Apple Developer Academy | Mackenzie

Laboratório de Ensino e Pesquisa especializado em iOS / tvOS / watchOS

Lucas Fernandez Nicolau
Lucas Fernandez Nicolau

Written by Lucas Fernandez Nicolau

iOS Developer passionated about consistent software combined with a beautiful UI and a great UX. Currently working @ BTG Pactual