Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. iOS

iOS From Scratch com Swift: Navigation Controllers e Hierarquias da View Controller

by
Difficulty:BeginnerLength:LongLanguages:
This post is part of a series called iOS From Scratch With Swift.
iOS From Scratch With Swift: Table View Basics
iOS From Scratch With Swift: Exploring Tab Bar Controllers

Portuguese (Português) translation by David Batista (you can also view the original English article)

No iOS, a navigation controller é uma das principais ferramentas para apresentação de múltiplas telas de conteúdo. Este artigo ensina você a como usar as navigation controllers, criando um aplicativo para mostrar os livros de uma biblioteca.

Introdução

No tutorial anterior, você aprendeu que a classe table view do UIKit é ótima para apresentação tabular ou colunar de dados. Quando o conteúdo precisa se espalhar em múltiplas telas, então, uma navigation controller é frequentemente a ferramenta escolhida. A classe UINavigationController implementa este tipo de funcionalidade.

Como qualquer outra subclasse da UIViewController, uma navigation controller gerência uma view, uma instância da classe UIView. A view da navigation controller gerência várias subviews, incluindo uma navigation bar na parte superior, uma view com um conteúdo customizado e uma toolbar opcional na parte inferior. O que faz uma navigation controller especial é que ela cria e gerência uma hierarquia de view controllers, frequentemente referenciada como pilha de navegação (navigation stack).

Neste artigo, criaremos um novo aplicativo iOS para nos familiarizarmos com a classe UINavigationController. Você aprenderá que a combinação de uma navigation controller e uma pilha de (table) view controllers é uma elegante e poderosa solução para a apresentação de conjuntos de dados encadeados.

Em adição à UINavigationController, você também encontrará a UITableViewController neste tutorial, outra subclasse da UIViewController. A UITableViewController controla uma instância UITableView ao invés de uma instância UIView. As table view controllers automaticamente adota os protocolos UITableViewDataSource e UITableViewDelegate e que vai nos salvar algumas vezes.

Outro projeto

O aplicativo que vamos criar é chamado de Library. Com este aplicativo, usuários podem navegar em uma lista de autores e ver os livros que eles escreveram. A lista de autores é apresentada em uma table view.

Se o usuário tocar no nome de um autor, uma lista de livros escritos por este autor será apresentada. Semelhantemente, quando o usuário selecionar um titulo da lista de livros, outra view aparecerá, exibindo uma imagem da capa do livro. Vamos criar um novo projeto no Xcode para começar.

Criando o projeto

Abra o Xcode, crie um novo projeto selecionando New > Project... no menu File e selecione o template Single View Application na lista de templantes iOS > Application.

Choosing the Project Template

Chame o projeto de Library e atribua um organization name e identifier. Defina a Language como Swift e o Devices como iPhone. Informe ao Xcode onde você quer salvar o projeto e clique em Create.

Configuring the Project

O template Single View Application contém uma classe application delegate, AppDelegate, um storyboard, Main.storyboard e uma classe UIViewControllerViewController. Abra o arquivo AppDelegate.swift e dê uma olhada na implementação do método application(_:didFinishLaunchingWithOptions:). Sua implementação é curta e você já a conhece.

Adicionando recursos

Os arquivos fontes deste tutorial inclui os dados que iremos usar. Você pode procura-los na pasta chamada Resources. Ele inclui uma property list, Books.plist, contendo informações sobre os autores, os livros que eles escreveram, alguma informação sobre cada livro e uma imagem para cada livro existente na property list.

Arraste a pasta Resources para seu projeto e a adicione ao projeto. Quando você adicionar a pasta ao projeto o Xcode exibirá algumas opções. Certifique de marcar a opção Copy items if needed e não se esqueça de adicionar os arquivos á Library destino.

Add Resources to Project
Copy Items Into Destination Folder of Group

Property Lists

Antes de continuar, eu quero falar por um momento sobre property lists e o que são elas. Uma property list nada mais é que uma representação gráfica de um objeto. Como falamos anteriormente nesta série, um objeto gráfico é um grupo de objetos, formando uma rede através de conexões ou referencias compartilhadas entre eles.

É fácil de ler e escrever as property lists para e do disco, o que as tornam ideais para armazenar pequenas quantidades de dados. Quando trabalhamos com property lists, é importante lembrar que apenas alguns tipos de dados podem ser armazenados, tais como strings, números, datas, arrays, dicionarios e binários.

O Xcode torna a visualização das property lists muito simples. Selecione o Books.plist na pasta Resources que você adicionou ao projeto e visualize seu conteúdo usando a ferramenta editor de property list do Xcode. Isso será um ferramenta muito útil mais tarde neste artigo quando começarmos a trabalhar com o conteúdo do Books.plist.

Browsing a Property List in Xcode

Subclasse UITableViewController

Antes de começarmos a usar os dados armazenados no Books.plist, precisamos primeiro estabelecer algumas bases. Isso inclui a criação de uma view controller que gerêncie uma table view e que exibirá a lista de autores da property list.

No artigo anterior, criamos uma subclasse UIViewController e adicionamos uma table view à view da view controller para apresentar os dados ao usuário. Neste tutorial, vamos pegar uma atalho criando uma subclasse UITableViewController.

Comece removendo o ViewController.swift do seu projeto. Crie uma nova classe selecionando New > File... no menu File. Selecione o template Cocoa Touch Class na lista de templates iOS > Source.

Create a New Cocoa Touch Class

Chame a nova classe de AuthorsViewController e a torne uma subclasse da UITableViewController. Não precisa marcar a opção Also create XIB file for user interface, porque usaremos o storyboard para criar a interface do aplicativo.

Configure the New Cocoa Touch Class

Abra o Main.storyboard para substituir a view controller no storyboard por uma table view controller. Selecione a view controller no storyboard, aperte o tecla delete e arraste uma instância da UITableViewController do Object Library à direita. Selecione a nova view controller, abra a Identity Inspector na direita e atribua AuthorsViewController como sua classe. 

Adding a Table View Controller

No próximo artigo, usaremos protótipos de células para popular a table view. Neste tutorial, mostrarei a você uma alternativa. Selecione o objeto table view na workspace ou na lista de objetos a esquerda, abra o Attributes Inspector na direita e atribua 0 para Prototype Cells.

Remove Prototype Cells

Todo storyboard precisa de uma view controller inicial. Esta é a view controller que é instanciada quando o storyboard é carregado. Podemos marcar a authors view controller como a view controller inicial selecionando o objeto Authors View Controller na esquerda, abrindo o Attributes Inspector na direta e marcando a caixa de seleção Is Initial View Controller.

Populando a table view

Abra o AuthorsViewController.swift e examine o conteúdo do arquivo. Como a classe AuthorsViewController é uma subclasse da UITableViewController, a AuthorsViewController já esta em conformidade com os protocolos UITableViewDataSource e UITableViewDelegate.

Antes de podemos exibir dados na table view, precisamos dos dados para exibir. Como eu mencionei anteriormente, os dados contidos no Books.plist serve como o data source da table view. Parar usar estes dados, precisamos primeiro carrega-los em um objeto, um array para ser preciso.

Declaramos uma propriedade variável authors e atribuímos a ela um valor inicial de um array vazio do tipo [AnyObject]. Lembre-se que o tipo AnyObject pode representar qualquer classe ou estrutura.

O método viewDidLoad() da view controller é um ótimo lugar para carregar os dados da Books.plist na propriedade authors. Fazemos isso chamando o inicializador init(contentsOfFile:) da classe NSArray. Fazemos o downcast do objeto resultante para uma instância do tipo [AnyObject].

O método recebe um caminho de arquivo, o que significa que precisamos descobrir qual o caminho do Books.plist. O arquivo, Books.plist, é localizado no pacote do aplicativo, que é o diretório que contém o executável e os recursos do aplicativo, tais como imagens e sons.

Para obter o caminho do arquivo Books.plist, precisamos primeiro de uma referencia para o pacote principal do aplicativo chamando o mainBundle() da classe NSBundle. O próximo passo é pedir o caminho do aplicativo para o caminho de um de seus recursos, Books.plist. Chamamos o pathForResource(_:ofType:) na pasta principal do aplicativo, passando o nome e o tipo (extensão) do arquivo que nos interessa. Armazenamos o caminho do arquivo em uma constante, filePath.

Como é possível solicitarmos um recurso que não existe no pacote do aplicativo, o pathForResource(_:ofType:) retorna um opcional. Como regra geral, se um método pode retornar nil, deve ser usado um opcional. A constante filePath é do tipo String?. Pra desempacotar seguramente o opcional, usaremos o optional binding, que discutimos anteriormente nesta série.

Se colocarmos essas duas peças juntas, ficaremos com a seguinte implementação no viewDidLoad(). Eu também adicionei uma instrução print para exibir o conteúdo da propriedade authors no console. Isso nos permite ver seu conteúdo.

Se você leu o artigo anterior desta série, então popular a table view deve ser simples. Como a table view tem apenas uma seção, a implementação do numberOfSectionsInTableView(_:) é simples. A  AuthorsViewController herda a UITableViewController, que já esta em conformidade e implementações do protocolo UITableViewDataSource. Por isso que precisamos usar a palavra chave override. Estamos sobrepondo um método que é implementado pela classe pai.

O número de linhas na única seção da table view é igual ao número de autores no array authors, então tudo que precisamos fazer é contar os itens do array.

A implementação do tableView(_:cellForRowAtIndexPath:) é parecido com a que vimos no artigo anterior. A principal diferença é como buscamos a informação que exibiremos na table view cell.

O array de autores contém uma lista ordenada de dicionários, com cada dicionário contendo dois pares de chave-valor. O objeto para a chave Author é do tipo String, já o objeto para a chave Books é um array de dicionários com cada dicionário representando um livro escrito pelo autor. Abra o Books.plist no Xcode para examinar a estrutura do data source se isso não ficou claro.

Antes de implementarmos o tableView(_:cellForRowAtIndexPath:), precisamos cuidar de duas coisas. Primeiro, declaramos uma constante para o identificador de reuso de célula que iremos usar.

Segundo, chamaremos o registerClass(_:forCellResuseIdentifier:) na table view, passando a UITableViewCell.classForCoder() e o identificador de reuso da célula. Chamamos este método na viewDidLoad() para certificar que será chamado apenas uma vez.

Com isso feito, a implementação do tableView(_:CellForRowAtIndexPath:) se torna muito simples.

Note que ligamos os optional bindings com uma virgula. Esta é uma abordagem conveniente para evitar o encadeamento de if. Pedimos ao authors pelo elemento no indexPath.row e efetuamos o downcast para [String: AnyObject]. Como precisamos do nome do autor, pedimos ao author pelo valor na chave "Author", efetuamos o downcast no resultado para String.

Adicionando uma Navigation Controller

Adicionar um navigation controller é fácil usando um storyboard. Entretanto, antes de adicionarmos um navigation controller, é importante entender como os navigation controllers funcionam no iOS.

Como qualquer outra subclasse UIViewController, uma navigation controller gerência uma instância UIView. A view do navigation controller gerência muitas subviews incluindo uma barra de navegação na parte superior, uma view que contém um conteúdo personalisado e uma toolbar opcional na parte inferior. O que torna uma navigation controller única é que ela gerência uma pilha de view controllers.

Navigation Stack

A termo pilha quase pode ser assumido literalmente. Quando uma navigation controller é inicializada, a navigation controller chama uma view controller raiz. A view controller raiz é a view controller na parte inferior da pilha de navegação.

Ao inserir (pushing) outra view controller na pilha de navegação, a view da view controller raiz é substituída com a view de uma nova view controller. Quando se esta trabalhando com navigation controllers, a view apresentada sempre é a view da view controller mais acima na pilha de navegação.

Quando uma view controller é removida (popped) da pilha de navegação, a view da view controller abaixo é apresentada novamente. Ao inserir ou remover view controllers na/de uma pilha de navegação do navigation controller, uma hierarquia de views é criada e, como resultado, um conjunto de dados encadeados podem ser apresentados ao usuário. Vamos ver como tudo isso, inserir e remover, funciona na prática.

Entre novamente no storyboard do projeto (Main.storyboard) e selecione a view controller. Para adicionar uma view controller para o mix, selecione Embed in > Navigation Controller no menu Editor. Algumas coisas mudam:

  • o navigation controller passa a ser a view controller inicial do storyboard
  • uma nova cena chamada Navigation Controller Scene é adicionada
  • uma navigation bar é adicionada ao navigation e authors view controller
  • a navigation controller e a authors view controler serão conectadas por uma segue
Adding a Navigation Controller

Segues são comuns nos storyboards e aprenderemos mais sobre elas mais tarde nesta série. Há vários tipos de segues e a segue que conecta a navigation controller e a authors view controller é uma relationship segue.

Toda navigation controller tem uma view controller raiz, a view controller na parte inferior da pilha de navegação. Ela não pode ser removida da pilha de navegação, porque uma navigation controller sempre precisa de uma view controller para exibir ao usuário. A relationship segue entre a navigation controller e a authors view controller simboliza que a ultima é a view controller raiz da navigation controller.

A navigation bar na parte superior da navigation controller e da authors view controller é algo que você recebe automaticamente quando se está trabalhando com navigation controllers. É uma instância da UINavigationBar e ajuda a navegar pela pilha de navegação.

Apesar da navigation controller ser a view controller inicial do storyboard, a authors view controller é a primeira view controller que veremos quando o aplicativo iniciar. Como mencionei anteriormente, a navigation controller nada mais é que um empacotador que ajuda na navegação entre uma hierarquia de view controllers. Está view é populada pelas views das view controllers na pilha de navegação.

Para adicionar um titulo à navigation bar, adicione a seguinte linha no método viewDidLoad() da classe AuthorsViewController.

Toda view controller tem uma propriedade title que é usada em vários lugares. A navigation bar é um deles. Execute o aplicativo e veja o resultado dessa pequena alteração.

Inserindo e Removendo

Vamos adicionar agora a habilidade de ver uma lista de livros quando o usuário toca no nome de um autor. Isso significa que precisamos capturar a seleção (o nome do autor), instanciar uma nova view controller baseado na seleção e inserir a nova view controller na pilha de navegação. Isso parece complicado? Não é. Deixe-me te mostrar.

Outra Table View Controller

Por que não exibir a lista de livros em outra table view. Cria uma nova subclasse da UITableViewController e chame de BooksViewController.

Create the BooksViewController Class

Como vimos anteriormente, carregar a lista de livros é fácil, mas como a books view controller sabe qual autor foi tocado pelo usuário? Há várias formas de informar à nova view controller sobre a seleção do usuário, mas o método que a Apple recomenda é conhecido como passar por referência. Como isso funciona?

A books view controller declara uma propriedade author que podemos atribuir para configurar a books view controller. A books view controller usa a propriedade author para exibir os livros do autor selecionado. Abra o BooksViewController.swift e adiciona uma propriedade variável do tipo [String: AnyObject]! e chame de author.

Por que não declaramos a author como uma [String: AnyObject]? ou [String: AnyObject]? Como uma variável precisa ter um valor inicial, não podemos declarar a author como [String: AnyObject]. Poderíamos usar [String: AnyObject]?, mas isso significa que teríamos que desempacotar o opcional toda vez que quisermos acessar seu valor.

Em Swift, você normalmente irá forçar o desempacotamente de opcionais se você souber que a propriedade tem um valor e, mais importante, se ela precisa ter uma valor para que seu aplicativo funcione como esperado. Se a propriedade author não tiver um valor, então a books view controller será de pouca utilidade para nós ja que ela não seria capaz de exibir qualquer informação.

Para facilitar o acesso aos livros do autor, também declaramos uma propriedade computada. Como o nome sugere, uma propriedade computada não armazena um valor. Ela define um getter e/ou setter para acessar ou atribuir o valor de outra propriedade. Dê uma olhada na propriedade computada books a seguir.

O valor da books depende do valor da author. Verificamos se a author tem um valor para a chave "Books" e efetuamos o downcast do valor para um array de objetos AnyObject. Se a author não tiver um valor para "Books", criamos um array vazio do tipo [AnyObject]. Como a propriedade computada books define apenas um getter, podemos simplificar a implementação conforme abaixo:

O resto da classe BooksViewController é simples. Dê uma olhada nas implementações do três método do protocolo UITableViewDataSource mostrados abaixo.

Isso também significa que precisamos declarar uma propriedade constante para o identificador de reuso da célula e registrar uma classe como célula de reuso na viewDidLoad(). Não há nada de novo.

Inserindo uma View Controller

Quando o usuário toca no nome de um autor na authors view controller, o aplicativo deve exibir a lista de livros escritos pelo autor. Isso significa que precisamos instanciar a classe BooksViewController, informar à instancia qual autor foi selecionado pelo usuário e inserir a nova view controller na pilha de navegação.

O storyboard irá nós ajudar com isso. Abra o Main.storyboard, arraste outra instância UITableViewController do Object Library e atribua a classe BooksViewController no Identify Inspector.

Add Books View Controller to Storyboard

Selecione a table view na nova view controller e atribua 0 para o número de Prototype Cell na Attributes Inspector. Para inserir a books view controller na pilha de navegação da navigation controller, precisamos criar outra segue. Desta vez, no entendo, criamos uma segue manualmente, uma show segue para ser mais preciso. 

Selecione a authors view controller no storyboard, segure a tecla Control e arraste da authors view controller para a books view controller. Selecione Manual Segue > Show no menu que aparecer para criar uma segue da authors view controller para a books view controller.

Create a Manual Show Segue

Há mais uma coisa que precisamos antes de retornar à implementação da books view controller. Selecione a segue que criamos, abra o Attributes Inspector na direita e atribua BooksViewController para o Identifier da segue. Ao dar um nome à segue, podemos nos referir a ela mais tarde no código.

Setting the Segue Identifier

Para colocar a segue em uso, precisamos implementar o tableView(_:didSelectRowAtIndexPath:) na authors view controller. Este método é definido no protocolo UITableViewDelegate, como vimos no artigo anterior sobre table view. Neste método, chamamos o performSegueWithIdentifier(_:sender:) para processar a segue que criamos no storyboard. O método performSegueWithIdentifier(_:sender:) recebe dois argumentos, o identificador da segue e o remetente da mensagem. Ficou claro agora por que demos um identificador para a segue no storyboard? Perceba também que reiniciamos a seleção após executar a segue.

A constante SegueBooksViewController é outra propriedade constante da classe AuthorsViewController.

Antes de uma segue ser executada, a view controller tem a oportunidade de se preparar para a segue no prepareForSegue(_:sender:). Neste método, a view controller pode configurar a view controller destino, a book view controller. Vamos implementar o prepareForSegue(_:sender:) para ver como ele funciona.

Este método é chamado sempre que uma segue for executada. Primeiro verificamos se o identificador da segue é igual a SegueBooksViewController. Então pedimos a table view pelo índice da seleção atual usando optional binding. Se uma linha estiver selecionada, pedimos ao authors pelo autor que corresponde a seleção.

Na instrução if, obtemos uma referência para a books view controller (a view controller destino da segue) e atribuímos o autor selecionado na table view à sua propriedade author.

Você pode estar se perguntando onde ou quando inicializamos a books view controller? Não instanciamos explicitamente uma instância da books view controller. O storyboard sabe qual classe ele precisa instanciar e inicializa uma instancia da BooksViewController para nós.

Antes de você executar seu aplicativo, abra o BooksViewController.swift e atribua o titulo da view controller para o nome do autor, para atualizar o titulo da navigation bar.

Execute o aplicativo. Toque no nome de um autor na table view e observe como a nova instancia BooksViewController é inserida na pilha de navegação e exibida ao usuário. Você percebeu que também apareceu um botão para retornar quando usamos uma navigation controller. O titulo da view controller anterior é usado como nome do botão de voltar.

Adicionando uma capa do livro

Quando o usuário tocar em um livro na books view controller, o aplicativo deverá mostrar a capa do livro. Não usaremos uma table view controller para isso. Ao invés, usaremos uma simples subclasse UIViewController e exibiremos a capa do livro em uma instância da classe UIImageView. A UIImageView é uma subclasse da UIView especializada em exibir imagens.

Crie uma nova subclasse da UIViewController - não UITableViewController - e chame ela de BookCoverViewController.

Creating the BookCoverViewController Class

Precisamos declarar duas propriedades de armazenamento na nova view controller. A primeira propriedade de armazenamento é uma referencia para a image view que vamos usar para exibir a capa do livro. A palavra chave @IBOutlet indica que iremos fazer uma conexão no storyboard. A segunda propriedade de armazenamento, Book, é do tipo [String: String]!. Esta propriedade representa o livro que será exibido na book cover view controller.

Abra o Main.storyboard para criar a interface da book cover view controller. Arraste uma instância UIViewController da Object Library para a workspace e atribua a classe BookCoverViewController no Identify Inspector.

Adding a View Controller to the Workspace

Arraste uma instância da UIImageView da Object Library para a view da view controller e deixe ela cobrindo a view inteira. No Connections Inspector, a conecte com a outlet bookCoverView da view controller.

Adding an Image View

Para certificar que o image view será exibido corretamente em todos os dispositivos, precisamos aplicar as restrições de layout necessarias como mostrado abaixo.

Adding Layout Constraints to the Image View

Antes de implementar a view controller, crie uma push segue entre a books view controller e a book cover view controller. Selecione a segue e atribua seu identificador como BookCoverViewController na Attributes Inspector.

Creating a Segue to the Book Cover View Controller

Na classe BooksViewController, declare uma propriedade constante para o identificador da segue.

Usaremos esta propriedade no tableView(_:didSelectRowAtIndexPath:) para executar a segue que criamos no storyboard. Não esqueça de tirar a seleção da linha após a execução da segue.

A implementação da prepareForSegue(_:vender:) será parecida com a da classe BooksViewController. Verificamos se o identificador da segue é igual a SegueBookCoverViewController e pedimos à table view o índice da linha selecionada. Pedimos ao books pelo livro que corresponde a seleção do usuário e atribuímos a propriedade book da view controller destino, uma instância da BookCoverViewController

Configuramos a image view da classe BookCoverViewController em seu método viewDidLoad(). Pedimos ao book pelo valor na chave "Cover" e instanciamos um objeto UIImage chamando o inicializador init(named:), passando o nome do arquivo. Atribuímos ao objeto UIImage à propriedade image de bookCoverView.

Na viewDidLoad(), também definimos o content mode da image view para ScaleAspectFit. A propriedade contetMode é do tipo UIViewContentMode, uma enumeração. O valor que atribuimos, ScaleAspectFit, diz ao image view esticar a imagem tanto quanto possível, respeitando sua proporção de tamanho.

Execute o aplicativo e passeie por ele. Agora você deve ser capaz de ver os livros armazenados na Books.plist.

Onde está a remoção?

Anteriormente neste artigo, eu expliquei que a view controller pode ser inserida na ou removida de uma pilha de navegação. Até agora, apenas inserimos view controllers na pilha de navegação. Remover uma view controller de uma pilha de navegação ocorre quando o usuário toca no botão voltar da navigation bar. Esta é outra pequena funcionalidade que vem automaticamente.

Em algum momento, entretanto, você irá ter uma situação em que você precisa remover uma view controller manualmente de uma pilha de navegação. Você pode fazer isso chamando o popViewControllerAnimated(_:) na navigation view controller. Isso remove a view controller mais acima de uma pilha de navegação.

Outra alternativa, você pode remover todas as view controllers de uma pilha de navegação - com exceção da view controller raiz - chamando o popToRootViewControllerAnimated(_:) na navigation controller.

Como você acessa a navigation controller de uma view controller? A classe UIViewController declara uma propriedade computada, navigationController, do tipo UINavigationController?. Se a view controller estiver em uma pilha de navegação, então essa propriedade faz referência a navigation controller da pilha de navegação que ela pertence.

Conclusão

Eu espero que você concorde que as navigation controller não são complicadas. Este artigo poderia ter sido um pouco mais curto, mas eu espero que você tenha aprendido algumas coisas no meio do caminho. No próximo artigo, vamos dar uma olhada nas tab bar controllers. Embora as tab bar controllers também gerenciem uma coleção de view controllers, elas são um pouco diferente das navigation controllers.

Se você tiver algumas perguntas ou comentários, você pode deixa-los nos comentários abaixo ou me procurar no Twitter.

Seja o primeiro a saber sobre novas traduções–siga @tutsplus_pt no Twitter!

Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.