Criando uma Pokedex com Swift Part: 3/3

Mostrando nossos monstrinhos

Recapitulando a parte anterior.

Nós criamos toda uma estrutura com models, parses e requests para consumir um JSON e exibir seu resultado no console do Xcode.

Hoje finalizando essa nossa pequena série, vamos usar uma TableViewController para exibir o resultado desse JSON que atualmente está só no console e finalizar nossa linda e maravilhosa Pokedex.

Criando o Layout

Para começar nossa simples exibição vamos abrir o storyboard que foi criado com o projeto, o Main.Storyboard.

Procure na Object Library no lado direito inferior da tela um NavigationController e arraste esse item para o storyboard.

Selecione a ViewController que já estava adicionada e a exclua-a.

Selecione a NavigationController que adicionamos na tela e abra o menu Attributes inspector do lado direito superior.

Procure pela sessão de ViewController e habilite a opção Is Initial View Controller.

Volte na Object Library e procure por TableViewCell e arraste duas delas para dentro da nossa TableViewController que foi adicionado junto ao NavigationController.

Uma célula vai ser a do pokemon, uma vai ser de load e a outra vai ser uma célula vazia, que vai ser exibida caso de algum erro.

Vamos começar pelo simples, pela célula de load.

Arraste da Object Library um Activity Indicator View para uma das células.

Para ter certeza que esse objeto sempre estará no centro, vou adicionar duas constraints de alinhamento.

Selecione o objeto e siga a imagem abaixo.

Constraints de alinhamento

Agora é a vez da célula que vai exibir nossos queridos e amados pokemons.

Para ela vamos precisar de uma UIImageView e duas UILabels. Não vou entrar na parte de constraints porque a construção do visual fica a critério de cada um. Mas para quem tiver curiosidade, deixei o meu assim.

A última coisa que falta para terminar nosso layout é atribuir os identifiers das células. Os identifiers são os nomes das células e usaremos isso no código.

Selecione a célula dos pokemons, no lado direito superior selecione o Attribute Inspector, e procure pela sessão Table View Cell e mude o Identifier dela para o nome que você preferir, mas lembre-se esse nome será usado na hora do código.

Célula do Pokemon

Agora faça a mesma coisa com as duas outras células, a de load e a vazia, colocando outros nomes.

Célula de Load
Célula Vazia

Muito bem pessoas, com tudo configurado chegamos na hora de programar.

Codificando

Começando pelo mais fácil, vamos criar duas classes que irão mostrar o layout da nossa célula de load e da célula de pokemon.

Crie uma TableViewCell chamada PokemonViewCell, essa célula vai montar o layout da célula do pokemon.

Vamos criar uma função chamada configureCell que irá receber o model do Pokemon e a imagem que será carregada.

class PokemonViewCell: UITableViewCell {
func configureCell(withModel model: PokemonModel, pokemonSpriteData data:Data) {
//TODO: Adicionar campos
}
}

Mas está faltando uma coisa nesse código, que são os campos para que seja possível modificar os valores, para resolver isso vamos criar Outlets.

Vá para o storyboard, selecione a célula do pokemon, no lado direito superior abra o identity inspector e digite o nome da classe que acabamos de criar.

Abra o assistant editor no lado direito superior, irá dividir a tela em dois.

Selecione o imageView da célula, segure o control e o botão esquerdo do mouse e arraste para dentro do código em cima da função que criamos.

Criação de um Outlet

Coloque um nome para esse Outlet e depois repita isso para todos os elementos editáveis da célula, ficará algo parecido com o código abaixo.

@IBOutlet weak var imgView: UIImageView!
@IBOutlet weak var pokemonIdLb: UILabel!
@IBOutlet weak var pokemonNameLb: UILabel!
func configureCell(withModel model: PokemonModel,pokemonSpriteData data:Data) { .......

Agora podemos editar esses campos dentro da nossa função de configuração. O código completo da função irá ficar assim:

public func configureCell(withModel model: PokemonModel, pokemonSpriteData data:Data) {
self.pokemonIdLb.text = "#\(model.id)"
self.pokemonNameLb.text = model.name
self.imgView.image = UIImage(data: data)
}

Falta criar uma classe para nossa célula de load.

Crie um arquivo chamado LoadViewCell e crie um Outlet para o ActivityIndicatorView que tem dentro dele. A classe ficará bem simples.

class LoadViewCell: UITableViewCell {
@IBOutlet weak var loadActivity: UIActivityIndicatorView!
}

Ótimo, já temos nossos layouts prontos para receber os valores, para isso vamos criar uma classe chamada PokedexViewController, que irá ser relacionada a nossa TableViewController do storyboard.

Vamos deixar para criar as funções responsáveis pelo controle da tabela por último, primeiro vamos criar as variáveis que vamos usar para controle e requests.

O código ficará assim.

class PokedexViewController: UITableViewController {
    //Variável responsável pelos Requests
var requestPokedex: ResquestPokedex = ResquestPokedex()
//Guarda todos os pokemons
var resultModel: PokedexModel?
//Guarda a quantidade de resultados que serão exibidos
var resultCount = 0
//Guarda as informações de cada pokemon separadamente
var pokemons = [PokemonModel]()
//Guarda as imagens de cada pokemon separadamente
var imagePokemons = [Data]()
    override func viewDidLoad() {
super.viewDidLoad()
}
}

Próxima etapa é criar as funções responsáveis pelos requests, serão três funções, uma para todos os pokemons, uma para cada pokemon pegar informações mais especificas e uma para as imagens.

Request de todos os pokemons

fileprivate func loadPokedex(url: String?)
{
requestPokedex.getAllPokemons(url: url) { (response) in
switch response
{
case .success(let model):
//Passa o model geral para nossa variavel
self.resultModel = model
//Essa função será criada futuramente
//Pedimos para carregar um pokemon especifico, estamos passando self.resultCount+1 porque os ids dos pokemons na API começam a partir do 1
self.loadPokemon(self.resultCount+1)
//Incrementamos a quantidade de resultados que serão exibidos
self.resultCount += model.results
         case .serverError(let description):
print(description)
case .timeOut(let description):
print(description)
case .noConnection(let description):
print(description)
}
}
}

Request de um pokemon específico

Depois de carregar todos os pokemons com paginação, hora de carregar um pokemon por vez para pegar suas informações mais especificas como a imagem dele.

fileprivate func loadPokemon(_ id: Int)
{
requestPokedex.getPokemon(id: id) { (response) in
switch response
{
case .success(let model):
//Adiciona o pokemon em nossa variavel
self.pokemons.append(model)
//Manda fazer load da imagem do pokemon carregado
self.loadImagePokemon(url: model.urlImage)
case .serverError(let description):
print(description)
case .timeOut(let description):
print(description)
case .noConnection(let description):
print(description)
}
}
}

Request da imagem do pokemon

O ultimo request é o da imagem dos nossos poke monsters.

fileprivate func loadImagePokemon(url: String)
{
requestPokedex.getImagePokemon(url: url) { (response) in
switch response
{
case .success(let model):
//Salva a imagem em nossa variavel
self.imagePokemons.append(model)
//If inline
//Se o ultimo pokemon carregado ainda nao for o ultimo pokemon da lista de todos pokemons que nos temos, mandamos ele carregar o proximo pokemon
//Se já tiver sido carregado todos os pokemons eu mando atualizar a tabela
self.pokemons.last!.id < self.resultCount ?
self.loadPokemon(self.pokemons.last!.id + 1) :
self.tableView.reloadData()
case .noConnection(let description):
print(description)
case .serverError(let description):
print(description)
case .timeOut(let description):
print(description)
}
}
}

Controles de tabela

Já temos as funções responsáveis pelos requests, agora falta implementar as funções que irão fazer o controle de tabela.

Vamos usar 3 funções de controle da tabela, um para dizer quantos itens a tabela terá, um para montar a célula e um quando a célula entra na tela.

O primeiro é para dizer quantos itens nossa tabela terá.

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
//Se tiver próxima página será adicionado a quantidade de resultados mais uma célula que irá carregar mais pokemons
return resultModel?.next == "" ? resultCount : resultCount + 1
}

Assim nossa tabela saberá quantas células ela vai exibir. Mas esse +1, como vamos carregar mais pokemons quando chegar a hora?

para isso vamos ter a função willDisplay da tabela.

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row == resultCount {
loadPokedex(url:resultModel?.next)
}
}

Quando o ultimo resultado aparecer vamos pedir para carregar mais pokemons, sem travar o app.

Agora o último e mais importante, vamos fazer aparecer o visual das células na tabela.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  //Se for a ultima célula carrego a célula de load
if indexPath.row == resultCount {
guard let cellLoad = tableView.dequeueReusableCell(withIdentifier: "loadCell", for: indexPath) as? LoadViewCell else {
return tableView.dequeueReusableCell(withIdentifier: "emptyCell", for: indexPath)
}
    //Inicio a animação de load
cellLoad.loadActivity.startAnimating()
return cellLoad
}
  //Se tiver tudo ok carrego a célula de pokemon
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? PokemonViewCell else {
return tableView.dequeueReusableCell(withIdentifier: "emptyCell", for: indexPath)
}
  //Configuro o visual da célula
cell.configureCell(withModel: pokemons[indexPath.row], pokemonSpriteData: imagePokemons[indexPath.row])
  return cell
}

Tudo feito, agora só dar o famoso build (Command+R) eeeee…………

Nada irá acontecer, tem um último passo.

Volte para o storyboard, selecione a TableViewController, abra o identity inspector e digite o nome da classe PokedexViewController no campo de class.

Agora sim companheiros, podemos dar uma build (Command+R).

Como não fizemos do jeito mais otimizado, o tempo de load vai demorar um pouco, mas se você tiver paciência logo verá seus incríveis pokemons.

Olha só…. como mágica.

Para quem quiser o link do projeto completo está aqui.

Assim finalizamos nossa Pokedex e essa sequencia de tutoriais. Espero que tenha ajudado.

Obrigado por me acompanhar nessa jornada Pokemon


Caso tenha gostado do texto, recomende-o clicando no coração ali em baixo. Obrigado e até a próxima.

Até a Proxima