iOS From Scratch com Swift: Navigation Controllers e Hierarquias da View Controller
() translation by (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.



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.



O template Single View Application contém uma classe application delegate, AppDelegate
, um storyboard, Main.storyboard e uma classe UIViewController
, ViewController
. 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.
1 |
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { |
2 |
return true |
3 |
}
|
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.






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.



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.



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.



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.



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.



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]
.
1 |
authors = NSArray(contentsOfFile: path) as! [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
.
1 |
let filePath = NSBundle.mainBundle().pathForResource("Books", ofType: "plist") |
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.
1 |
override func viewDidLoad() { |
2 |
super.viewDidLoad() |
3 |
|
4 |
let filePath = NSBundle.mainBundle().pathForResource("Books", ofType: "plist") |
5 |
|
6 |
if let path = filePath { |
7 |
authors = NSArray(contentsOfFile: path) as! [AnyObject] |
8 |
print(authors) |
9 |
}
|
10 |
}
|
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.
1 |
override func numberOfSectionsInTableView(tableView: UITableView) -> Int { |
2 |
return 1 |
3 |
}
|
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.
1 |
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { |
2 |
return authors.count |
3 |
}
|
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.
1 |
import UIKit |
2 |
|
3 |
class AuthorsViewController: UITableViewController { |
4 |
|
5 |
let CellIdentifier = "Cell Identifier" |
6 |
|
7 |
var authors = [AnyObject]() |
8 |
|
9 |
...
|
10 |
|
11 |
}
|
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.
1 |
override func viewDidLoad() { |
2 |
super.viewDidLoad() |
3 |
|
4 |
let filePath = NSBundle.mainBundle().pathForResource("Books", ofType: "plist") |
5 |
|
6 |
if let path = filePath { |
7 |
authors = NSArray(contentsOfFile: path) as! [AnyObject] |
8 |
print(authors) |
9 |
}
|
10 |
|
11 |
tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: CellIdentifier) |
12 |
}
|
Com isso feito, a implementação do tableView(_:CellForRowAtIndexPath:)
se torna muito simples.
1 |
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { |
2 |
// Dequeue Resuable Cell
|
3 |
let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier, forIndexPath: indexPath) |
4 |
|
5 |
if let author = authors[indexPath.row] as? [String: AnyObject], let name = author["Author"] as? String { |
6 |
// Configure Cell
|
7 |
cell.textLabel?.text = name |
8 |
}
|
9 |
|
10 |
return cell; |
11 |
}
|
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.



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



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
.
1 |
// Set Title
|
2 |
title = "Authors" |
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
.



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
.
1 |
import UIKit |
2 |
|
3 |
class BooksViewController: UIViewController { |
4 |
|
5 |
var author: [String: AnyObject]! |
6 |
|
7 |
...
|
8 |
|
9 |
}
|
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.
1 |
var books: [AnyObject] { |
2 |
get { |
3 |
if let books = author["Books"] as? [AnyObject] { |
4 |
return books |
5 |
} else { |
6 |
return [AnyObject]() |
7 |
}
|
8 |
}
|
9 |
}
|
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:
1 |
var books: [AnyObject] { |
2 |
if let books = author["Books"] as? [AnyObject] { |
3 |
return books |
4 |
} else { |
5 |
return [AnyObject]() |
6 |
}
|
7 |
}
|
O resto da classe BooksViewController
é simples. Dê uma olhada nas implementações do três método do protocolo UITableViewDataSource
mostrados abaixo.
1 |
override func numberOfSectionsInTableView(tableView: UITableView) -> Int { |
2 |
return 1 |
3 |
}
|
4 |
|
5 |
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { |
6 |
return books.count |
7 |
}
|
8 |
|
9 |
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { |
10 |
// Dequeue Resuable Cell
|
11 |
let cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier, forIndexPath: indexPath) |
12 |
|
13 |
if let book = books[indexPath.row] as? [String: String], let title = book["Title"] { |
14 |
// Configure Cell
|
15 |
cell.textLabel?.text = title |
16 |
}
|
17 |
|
18 |
return cell; |
19 |
}
|
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.
1 |
import UIKit |
2 |
|
3 |
class BooksViewController: UITableViewController { |
4 |
|
5 |
let CellIdentifier = "Cell Identifier" |
6 |
|
7 |
...
|
8 |
|
9 |
}
|
1 |
override func viewDidLoad() { |
2 |
super.viewDidLoad() |
3 |
|
4 |
tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: CellIdentifier) |
5 |
}
|
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
.



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.



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.



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.
1 |
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { |
2 |
// Perform Segue
|
3 |
performSegueWithIdentifier(SegueBooksViewController, sender: self) |
4 |
|
5 |
tableView.deselectRowAtIndexPath(indexPath, animated: true) |
6 |
}
|
A constante SegueBooksViewController
é outra propriedade constante da classe AuthorsViewController
.
1 |
import UIKit |
2 |
|
3 |
class AuthorsViewController: UITableViewController { |
4 |
|
5 |
let CellIdentifier = "Cell Identifier" |
6 |
let SegueBooksViewController = "BooksViewController" |
7 |
|
8 |
...
|
9 |
|
10 |
}
|
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.
1 |
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { |
2 |
if segue.identifier == SegueBooksViewController { |
3 |
if let indexPath = tableView.indexPathForSelectedRow, let author = authors[indexPath.row] as? [String: AnyObject] { |
4 |
let destinationViewController = segue.destinationViewController as! BooksViewController |
5 |
destinationViewController.author = author |
6 |
}
|
7 |
}
|
8 |
}
|
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.
1 |
override func viewDidLoad() { |
2 |
super.viewDidLoad() |
3 |
|
4 |
if let name = author["Author"] as? String { |
5 |
title = name |
6 |
}
|
7 |
|
8 |
tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: CellIdentifier) |
9 |
}
|
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
.



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.
1 |
import UIKit |
2 |
|
3 |
class BookCoverViewController: UIViewController { |
4 |
|
5 |
@IBOutlet var bookCoverView: UIImageView! |
6 |
|
7 |
var book: [String: String]! |
8 |
|
9 |
...
|
10 |
|
11 |
}
|
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.



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.



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



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.



Na classe BooksViewController
, declare uma propriedade constante para o identificador da segue.
1 |
import UIKit |
2 |
|
3 |
class BooksViewController: UITableViewController { |
4 |
|
5 |
let CellIdentifier = "Cell Identifier" |
6 |
let SegueBookCoverViewController = "BookCoverViewController" |
7 |
|
8 |
...
|
9 |
|
10 |
}
|
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.
1 |
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { |
2 |
// Perform Segue
|
3 |
performSegueWithIdentifier(SegueBookCoverViewController, sender: self) |
4 |
|
5 |
tableView.deselectRowAtIndexPath(indexPath, animated: true) |
6 |
}
|
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
.
1 |
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { |
2 |
if segue.identifier == SegueBookCoverViewController { |
3 |
if let indexPath = tableView.indexPathForSelectedRow, let book = books[indexPath.row] as? [String: String] { |
4 |
let destinationViewController = segue.destinationViewController as! BookCoverViewController |
5 |
destinationViewController.book = book |
6 |
}
|
7 |
}
|
8 |
}
|
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
.
1 |
override func viewDidLoad() { |
2 |
super.viewDidLoad() |
3 |
|
4 |
if let fileName = book["Cover"] { |
5 |
bookCoverView.image = UIImage(named: fileName) |
6 |
bookCoverView.contentMode = .ScaleAspectFit |
7 |
}
|
8 |
}
|
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!