Dev (Back & Front)ARTIGO

Um exemplo do UINavigationController

O básico

O
UINavigationController te dá uma navegação passo-a-passo (como um assistente de
instalação) usando uma pilha de controllers. A Microsoft foi pioneira ao
introduzir isso no Vista com o painel de controle e a ideia de botões “para
trás”e “para frente”, e o 
UINavigationController não é muito diferente.

O
UINavigationController fica na janela e tem uma área para exibir cada
view/controller. De um ponto de vista ASP.NET MVC, é difícil se afastar da ideia de que o controller tem
múltiplas ações, cada uma delas exibindo uma visualização diferente. No mundo
do iPhone e do Cocoa Touch, cada controller tem apenas um View. É um mapeamento
1 para 1. Esse View pode conter vários views (Subviews), parecido com uma
árvore de controle, ou views parciais no ASP.NET MVC.

Então,
quando eu me referir ao controller, eu também estou falando sobre o nível
superior, container view.

O
UINavigationController tem uma propriedade Controllers
que é um array dos controllers que ele exibiu. O primeiro item no array é o primeiro controller exibido pelo UINavigationController. Cada controller é responsável
por “empurrar” o próximo controller que será exibido depois dele.

Portanto,
o controller raiz dentro do UINavigationController deve conter um botão. Quando
clicado, o botão irá “empurrar” o próximo controller a ser exibido. Os
controllers que você utilizar dentro do UINavigationController serão,
normalmente, um UIViewController ou um UITableViewController.

A hierarquia do
UIViewController

A cadeia de herança
se parece com isso:

1.UIViewController
2.- UITableController
3.- UINavigationController
4.- UITabBarController

Você
pode ver que o UINavigationController de
fato é herdado do UIViewController. O estranho sobre esse modelo de objeto é
que o UIViewController tem uma
propriedade chamada NavigationController. Portanto, para puristas de OO, é um pouco desconcertante
que uma classe pai esteja ciente de seus filhos. Mas, resmungos à parte, é assim
que é feito no UIKit. Como você
encontrará em um erro de execução, você não pode colocar um  UINavigationController dentro de um UINavigationController.

Sempre
que você “empurra” um controller, ele consegue acessar o pai UINavigationController – ele pode ou não
pertencer à propriedade NavigationController. A propriedade
é nula se o controller não estiver dentro de um UINavigationController.

Indo completamente sem XIB

Este
e os outros dois artigos que o acompanham são Construtores de Interface e livres
de NIB/XIB, no entanto, seu projeto
iPhone é sempre criado com um XIB no projeto – MainWindow.xib.
Ele contém a referência ao UIWindow em que a aplicação está contida, bem
parecido com o primeiro formulário em uma aplicação winforms.

Você pode deletar
esse arquivo XIB se quiser (isso pode melhorar marginalmente a
performance) e criar a janela manualmente. Sua classe  AppDelegate não precisa mais ser parcial, uma vez que o arquivo da view foi apagado:

01.public class Application
02.{
03.static void Main(string[] args)
04.{
05.// Make sure you use this overload
06.UIApplication.Main(args,null,"AppDelegate");
07.}
08.}
09.
10.[Register("AppDelegate")]
11.public class AppDelegate : UIApplicationDelegate
12.{
13.private UIWindow window;
14.private MyController controller;
15.
16.public override bool FinishedLaunching(UIApplication app, NSDictionary options)
17.{
18.var screenBounds = UIScreen.MainScreen.Bounds;
19.
20.controller = new MyController();
21.window = new UIWindow(screenBounds);
22.
23.window.AddSubview(controller.View);
24.window.MakeKeyAndVisible ();
25.
26.return true;
27.}
28.
29.// This method is required in iPhoneOS 3.0
30.public override void OnActivated (UIApplication application)
31.{
32.}
33.}

Tenha
certeza de que você removeu “Mainwindow.xib” de suas configurações do
projeto, a opção ‘Main interface file’ sob a seção “iPhone Application”.

No
entanto, para os propósitos deste artigo, eu irei manter o padrão do
projeto iPhone no Monotouch de ter uma
classe de design parcial.

Adicionando um
UINavigationController

Se você criar um novo
projeto iPhone no Monotouch, o primeiro
passo é adicionar uma nova classe, e adicionar seu UINavigationController.
Antes de fazer qualquer coisa, diga à sua janela principal que ela deve exibir
seu controller adicionando duas linhas à classe appdelegate:

01.public partial class AppDelegate : UIApplicationDelegate
02.{
03.private NavigationController controller;
04.public override bool FinishedLaunching (UIApplication app, NSDictionary options)
05.{
06.controller = new NavigationController();
07.window.AddSubview (controller.View);
08.window.MakeKeyAndVisible ();
09.
10.return true;
11.}
12.
13.// This method is required in iPhoneOS 3.0
14.public override void OnActivated (UIApplication application)
15.{
16.}
17.}

Tenha
certeza de que você não está usando variáveis locais para declarações do Controller.
Enquanto não existem problemas nisso quando você usa o Simulador do iPhone,
você corre o risco de ter erros de execução no iPhone, uma vez que a garbage collection é bem mais estrita e intensa. O Simulador do iPhone irá executar seu
aplicativo usando um mecanismo de garbage collection baseado na geração.
No entanto, no iPhone você está executando AOT (ahead of time – ‘à frente do
tempo’) compilado C#, e seus objetos são derreferenciados de uma maneira bem mais
estrita. Bem parecido com os Winforms, seus controles têm um membro privado
como referência, e no Monotouch a mesma coisa é aconselhável para todos os
controllers e views que você referencia em um controller.

Uma
coisa que deve ser notada sobre a abordagem que estou adotando neste artigo é
que estou criando uma nova classe para cada controller que estou usando. Você
não tem que fazer dessa forma – você pode simplesmente criar uma nova instância
do UIViewController e adicionar os códigos de controle e
layout para fazer isso dentro do seu  UINavigationController. No entanto, com essa
abordagem, você tem uma grande classe monolítica que executa todo o layout. Ao criar
classes separadas para cada controller, você separa as responsabilidades mais
claramente, fornecendo  melhor
saparação entre as preocupações
e maior coesão. Resumindo, você evita
que vários winforms sofram do efeito “espaguete”.

Abaixo está o NavigationController que a classe AppDelegate referencia:

01.public class NavigationController : UINavigationController
02.{
03.HomeViewController _firstController;
04.
05.public override void ViewDidLoad ()
06.{
07._firstController = new HomeViewController();
08.PushViewController(_firstController,true);
09.
10.base.ViewDidLoad ();
11.}
12.}

Depois disso, você está pronto para adicionar o
primeiro controller/view que o UINavigationController exibirá. Eu coloquei toda a minha definição em um
arquivo .cs no download da solução, no entanto, é mais limpo criar um arquivo
chamado “Controllers” e
colocar cada controller separado em seu próprio arquivo.

1.public class HomeViewController : UIViewController
2.{
3.public override void ViewDidLoad ()
4.{
5.}
6.}

Ao
clicar em executar, uma barra de navegação e uma visualização em branco irão ser
exibidos.

ViewDidLoad, ViewWillAppear,
ViewDidAppear

Se você estiver confuso sobre qual método de inicialização
do seu controller você deve sobrescrever, leia isto primeiro (inglês).

Adicionando controles
(Views) ao primeiro controller

Continuarei
usando o termo “controles” para me referir a um rótulo, botão, campo de texto,
mas, como já comentamos, a Apple os define como Views. O código abaixo demonstra
um rótulo básico e dois botões da barra de ferramentas do primeiro controller. Ele
centraliza os três controles, e cada botão “Empurra” clicado, consequentemente
exibe o próximo controller.

Você tem uma barra de
ferramentas e uma barra de navegação para esconder ou exibir o que você quiser
com cada controller dentro pai UINavigationController pai. O HomeViewController
garante que a barra de ferramentas seja exibida dentro do método ViewWillAppear. O motivo para isso é que o controller que o
primeiro botão “empurra” esconde a barra de ferramentas, então quando o usuário
clica para voltar para aquele controller (e retorna para o HomeViewController), a
barra de ferramentas precisa ser reexibida.

01.public class HomeViewController : UIViewController
02.{
03.public override void ViewDidLoad ()
04.{
05.Title = "Home";
06.
07.// A 'view' aka label control for this view
08.UILabel label = new UILabel();
09.label.Text = "HomeViewController";
10.label.Frame = Center(100,100);
11.label.BackgroundColor = UIColor.LightGray;
12.View.AddSubview(label);
13.
14.// 2 toolbar items
15.UIBarButtonItem item1 = new UIBarButtonItem();
16.item1.Title = "Click me";
17.item1.Clicked += delegate(object sender, EventArgs e) {
18.Level2ViewController controller = new Level2ViewController();
19.NavigationController.PushViewController(controller,true);
20.};
21.
22.UIBarButtonItem item2 = new UIBarButtonItem();
23.item2.Title = "View with no back";
24.item2.Clicked += delegate(object sender, EventArgs e) {
25.ViewWithNoBackController controller = new ViewWithNoBackController();
26.NavigationController.PushViewController(controller,true);
27.};
28.
29.ToolbarItems = new UIBarButtonItem[] {item1,item2};
30.
31.
32.base.ViewDidLoad ();
33.}
34.
35.public override void ViewWillAppear (bool animated)
36.{
37.// Re-show the toolbar here for consistency
38.NavigationController.SetToolbarHidden(false,true);
39.base.ViewWillAppear (animated);
40.}
41.
42.public RectangleF Center(float width,float height)
43.{
44.var rect = new RectangleF((View.Frame.Width / 2) - (width /2),(View.Frame.Height / 2) - (height /2),width,height);
45.
46.// Prints:
47.// Rect: {X=110,Y=180,Width=100,Height=100}
48.// Frame: {X=0,Y=20,Width=320,Height=460}
49.Console.WriteLine("Rect: {0}",rect);
50.Console.WriteLine("Frame: {0}",View.Frame);
51.
52.return rect;
53.}
54.}

Escondendo o botão voltar

O segundo botão na
barra de ferramentas “empurra” um novo controller, que demonstra como esconder
o botão voltar. Claro que sem a presença do botão voltar o usuário não pode
fazer nada a não ser deixar o aplicativo, mas isso é apenas para demonstrar
como escondê-lo. Você pode querer esconder o botão voltar em uma tela de carregamento
intermediária.

01.public class ViewWithNoBackController: UIViewController
02.{
03.public override void ViewDidLoad ()
04.{
05.Title = "Settings";
06.
07.// The label again
08.UILabel label = new UILabel();
09.label.Text = "ViewWithNoBackController";
10.label.Frame = new RectangleF(100,100,100,100);
11.View.AddSubview(label);
12.
13.// Demonstrates hiding the back and toolbars.
14.NavigationItem.SetHidesBackButton(true,true);
15.NavigationController.SetToolbarHidden(true,true);
16.
17.base.ViewDidLoad ();
18.}
19.
20.
21.}

Animando o
NavigationController.PushViewController

Para uma simples
transição de animações ao “empurrar” um novo view, você pode utilizar UIView.BeginAnimation
para ter quatro efeitos diferentes. Isso é bastante fácil de fazer, como ilustrado
abaixo. Para uma gama maior de animações, a classe CATransition é a melhor
opção – descrito
aqui
.

1.// Make sure you push first or the Title doesn't get animated.
2.var controller = new MyController();
3.NavigationController.PushViewController(controller, false);
4.
5.UIView.BeginAnimations(null,IntPtr.Zero);
6.UIView.SetAnimationDuration(1); UIView.SetAnimationTransition(UIViewAnimationTransition.FlipFromLeft,NavigationController.View,true);
7.UIView.CommitAnimations();

Se você quiser que a animação ocorra quando o
controller for ativado/desaparece, você terá que esconder o botão voltar e lidar
com o clique você mesmo (ou qualquer botão que você use para esconder o
controller atual). Isso pode ser feito com a maior parte do mesmo código:

1.UIView.BeginAnimations(null,IntPtr.Zero);
2.UIView.SetAnimationDuration(1);
3.UIView.SetAnimationTransition(UIViewAnimationTransition.FlipFromRight,NavigationController.View,true);
4.NavigationController.PopViewControllerAnimated(false);
5.UIView.CommitAnimations();

Substituindo a barra de
ferramentas

Quando você “empurra”
um novo controller, a barra de ferramentas é substituída. Se você quiser que a
mesma barra de ferramentas apareça como o último controller, uma solução é
derivar seus controllers de uma classe base que herda do UIViewController, e
então adicionar a barra de ferramentas no seu método ViewDidLoad. Alternativamente, você pode não
querer que a barra de ferramentas seja exibida de novo. O código abaixo
demonstra como adicionar uma nova barra de ferramentas, que quando clicada
“empurra” um novo controller.

01.public class Level2ViewController: UIViewController
02.{
03.public override void ViewDidLoad ()
04.{
05.Title = "Level 2";
06.
07.UILabel label = new UILabel();
08.label.Text = "Level2ViewController";
09.label.Frame = new System.Drawing.RectangleF(100,100,100,100);
10.View.AddSubview(label);
11.
12.// A new toolbar with items
13.UIBarButtonItem item = new UIBarButtonItem();
14.item.Title = "Another item";
15.item.Clicked += delegate(object sender, EventArgs e) {
16.Level3ViewController controller = new Level3ViewController();
17.NavigationController.PushViewController(controller,true);
18.};
19.
20.ToolbarItems = new UIBarButtonItem[] {item};
21.
22.base.ViewDidLoad ();
23.}
24.
25.public override void ViewWillAppear (bool animated)
26.{
27.// Re-show the toolbar here for consistency
28.NavigationController.SetToolbarHidden(false,true);
29.base.ViewWillAppear (animated);
30.}
31.}

Escondendo a barra de
ferramentas

Este é o último
controller, e ele esconde a barra de ferramentas quando carrega.

01.public class Level3ViewController: UIViewController
02.{
03.public override void ViewDidLoad ()
04.{
05.Title = "Level 3";
06.
07.// The label again
08.UILabel label = new UILabel();
09.label.Text = "Level3ViewController";
10.label.Frame = new System.Drawing.RectangleF(100,100,100,100);
11.View.AddSubview(label);
12.
13.// Hide the toolbar for this view
14.NavigationController.SetToolbarHidden(true,true);
15.
16.base.ViewDidLoad ();
17.}
18.}

Indo direto para um dos
controllers na pilha

Se a aplicação acabou
de iniciar, ou por qualquer outro motivo você queira pular direto para o último
controller dentro da cadeia (no meu exemplo, Level3ViewController), isso pode
ser conseguido ao criar, manualmente, o array NavigationController.ViewControllers.

01....
02.using System.Linq;
03.
04.public class NavigationController : UINavigationController
05.{
06.public override void ViewDidLoad ()
07.{
08.var firstController = new HomeViewController();
09.var secondController = new Level2ViewController();
10.var thirdController = new Level3ViewController();
11.
12.var viewControllers = ViewControllers.ToList();
13.viewControllers.Add(firstController);
14.viewControllers.Add(secondController);
15.viewControllers.Add(thirdController);
16.ViewControllers = viewControllers.ToArray();
17.
18.base.ViewDidLoad ();
19.}
20.}

Removendo um controller da
pilha (stack)

Para remover um
controller, usamos a mesma técnica que utilizada acima, mas removendo ao invés de
adicionando. Pode ser possível remover o atual também, p.e. RemoveAt(controllers.Length -1), mas eu não tentei fazer
isso.

01.public class Level3ViewController: UIViewController
02.{
03.public override void ViewDidLoad ()
04.{
05.var controllers = NavigationController.ViewControllers.ToList();
06.for (int i = 0; i < controllers.Count; i++)
07.{
08.if (controllers[i] is Level2ViewController)
09.controllers.RemoveAt(i);
10.}
11.NavigationController.ViewControllers = controllers.ToArray();
12.}
13.}

?

Texto original disponível em http://www.shrinkrays.net/articles/monotouch-controllers-by-example/uinavigationcontroller-by-example.aspx

É engenheiro de software em Londres e atualmente concentra seu trabalho em CRUD e em aplicativos web.

Ver perfil