Criando interfaces de usuário do iOS em código no Xamarin.iOS

A interface do usuário de um aplicativo iOS é como uma vitrine – o aplicativo normalmente recebe uma janela, mas pode preencher a janela com quantos objetos forem necessários, e os objetos e arranjos podem ser alterados dependendo do que o aplicativo deseja exibir. Os objetos nesse cenário – as coisas que o usuário vê – são chamados de exibições. Para criar uma única tela em um aplicativo, as exibições são empilhadas umas sobre as outras em uma hierarquia de exibição de conteúdo e a hierarquia é gerenciada por um único controlador de exibição. Aplicativos com várias telas têm várias hierarquias de exibição de conteúdo, cada uma com seu próprio controlador de exibição; o aplicativo coloca as exibições na janela para criar uma hierarquia de exibição de conteúdo diferente com base na tela na qual o usuário está.

O diagrama a seguir ilustra as relações entre a janela, exibições, subexibições e controlador de exibição que levam a interface do usuário para a tela do dispositivo:

Este diagrama ilustra as relações entre a Janela, Modos de Exibição, Subexibições e Controlador de Exibição

Essas hierarquias de exibição podem ser construídas usando o Construtor de Interface do Xcode, no entanto, é bom ter uma compreensão fundamental de como trabalhar inteiramente em código. Este artigo aborda alguns pontos básicos para começar a trabalhar com o desenvolvimento de interface do usuário somente de código.

Criando um projeto somente de código

Modelo de projeto em branco do iOS

Primeiro, crie um projeto iOS no Visual Studio usando o projeto File > New Project > Visual C# > iPhone & iPad > iOS App (Xamarin), mostrado abaixo:

Caixa de diálogo Novo Projeto

Em seguida, selecione o modelo de projeto Aplicativo em Branco:

Selecione uma caixa de diálogo de modelo

O modelo Projeto vazio adiciona 4 arquivos ao projeto:

Arquivos de projeto

  1. AppDelegate.cs - Contém uma UIApplicationDelegate subclasse, AppDelegate , que é usada para manipular eventos de aplicativos do iOS. A janela do aplicativo é criada no AppDelegatemétodo 's FinishedLaunching .
  2. Main.cs - Contém o ponto de entrada para o aplicativo, que especifica a classe para o AppDelegate .
  3. Info.plist - Arquivo de lista de propriedades que contém informações de configuração do aplicativo.
  4. Entitlements.plist – Arquivo de lista de propriedades que contém informações sobre os recursos e permissões do aplicativo.

Os aplicativos iOS são criados usando o padrão MVC. A primeira tela que um aplicativo exibe é criada a partir do controlador de exibição raiz da janela. Consulte o guia Hello, iOS Multiscreen para obter mais detalhes sobre o próprio padrão MVC.

A implementação para o AppDelegate adicionado pelo modelo cria a janela do aplicativo, da qual há apenas uma para cada aplicativo iOS, e a torna visível com o seguinte código:

public class AppDelegate : UIApplicationDelegate
{
    public override UIWindow Window
            {
                get;
                set;
            }

    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
    {
        // create a new window instance based on the screen size
        Window = new UIWindow(UIScreen.MainScreen.Bounds);

        // make the window visible
        Window.MakeKeyAndVisible();

        return true;
    }
}

Se você fosse executar este aplicativo agora, você provavelmente teria uma exceção lançada informando que Application windows are expected to have a root view controller at the end of application launch. Vamos adicionar um Controlador e torná-lo o Controlador de Exibição Raiz do aplicativo.

Adicionando um controlador

Seu aplicativo pode conter muitos Controladores de Exibição, mas precisa ter um Controlador de Exibição Raiz para controlar todos os Controladores de Exibição. Adicione um controlador à janela criando uma UIViewController instância e definindo-a para a Window.RootViewController propriedade:

public class AppDelegate : UIApplicationDelegate
{
    // class-level declarations

    public override UIWindow Window
    {
        get;
        set;
    }

    public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
    {
        // create a new window instance based on the screen size
        Window = new UIWindow(UIScreen.MainScreen.Bounds);

        var controller = new UIViewController();
        controller.View.BackgroundColor = UIColor.LightGray;

        Window.RootViewController = controller;

        // make the window visible
        Window.MakeKeyAndVisible();

        return true;
    }
}

Cada controlador tem uma exibição associada, que é acessível a partir da View propriedade. O código acima altera a propriedade da BackgroundColor exibição para UIColor.LightGray que ela fique visível, conforme mostrado abaixo:

O plano de fundo do Modo de Exibição é um cinza claro visível

Poderíamos definir qualquer UIViewController subclasse como a RootViewController desta maneira também, incluindo controladores de UIKit, bem como aqueles que nós mesmos escrevemos. Por exemplo, o código a seguir adiciona um UINavigationController como o RootViewController:

public class AppDelegate : UIApplicationDelegate
{
    // class-level declarations

    public override UIWindow Window
    {
        get;
        set;
    }

    public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
    {
        // create a new window instance based on the screen size
        Window = new UIWindow(UIScreen.MainScreen.Bounds);

        var controller = new UIViewController();
        controller.View.BackgroundColor = UIColor.LightGray;
        controller.Title = "My Controller";

        var navController = new UINavigationController(controller);

        Window.RootViewController = navController;

        // make the window visible
        Window.MakeKeyAndVisible();

        return true;
    }
}

Isso produz o controlador aninhado dentro do controlador de navegação, conforme mostrado abaixo:

O controlador aninhado no controlador de navegação

Criando um controlador de exibição

Agora que vimos como adicionar um controlador como o RootViewController da janela, vamos ver como criar um controlador de exibição personalizado no código.

Adicione uma nova classe com o nome CustomViewController mostrado abaixo:

A classe deve herdar de UIViewController, que está no UIKit namespace, conforme mostrado:

using System;
using UIKit;

namespace CodeOnlyDemo
{
    class CustomViewController : UIViewController
    {
    }
}

Inicializando o modo de exibição

UIViewController contém um método chamado ViewDidLoad que é chamado quando o controlador View é carregado pela primeira vez na memória. Esse é um local apropriado para fazer a inicialização do modo de exibição, como definir suas propriedades.

Por exemplo, o código a seguir adiciona um botão e um manipulador de eventos para pressionar um novo View Controller na pilha de navegação quando o botão é pressionado:

using System;
using CoreGraphics;
using UIKit;

namespace CodyOnlyDemo
{
    public class CustomViewController : UIViewController
    {
        public CustomViewController ()
        {
        }

        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();

            View.BackgroundColor = UIColor.White;
            Title = "My Custom View Controller";

            var btn = UIButton.FromType (UIButtonType.System);
            btn.Frame = new CGRect (20, 200, 280, 44);
            btn.SetTitle ("Click Me", UIControlState.Normal);

            var user = new UIViewController ();
            user.View.BackgroundColor = UIColor.Magenta;

            btn.TouchUpInside += (sender, e) => {
                this.NavigationController.PushViewController (user, true);
            };

            View.AddSubview (btn);

        }
    }
}

Para carregar esse controlador em seu aplicativo e demonstrar a navegação simples, crie uma nova instância do CustomViewController. Crie um novo controlador de navegação, passe sua instância do controlador de exibição e defina o novo controlador de RootViewController AppDelegate navegação para a janela como antes:

var cvc = new CustomViewController ();

var navController = new UINavigationController (cvc);

Window.RootViewController = navController;

Agora, quando o aplicativo é carregado, o é carregado dentro de CustomViewController um controlador de navegação:

O CustomViewController é carregado dentro de um controlador de navegação

Clicando no botão, irá empurrar um novo View Controller para a pilha de navegação:

Um novo View Controller empurrado para a pilha de navegação

Criando a hierarquia de exibição

No exemplo acima, começamos a criar uma interface de usuário no código adicionando um botão ao View Controller.

As interfaces de usuário do iOS são compostas por uma hierarquia de exibição. Modos de exibição adicionais, como rótulos, botões, controles deslizantes, etc. são adicionados como subexibições de alguns modos de exibição pai.

Por exemplo, vamos editar o CustomViewController para criar uma tela de login onde o usuário pode inserir um nome de usuário e senha. A tela será composta por dois campos de texto e um botão.

Adicionando os campos de texto

Primeiro, remova o botão e o manipulador de eventos que foram adicionados na seção Inicializando o modo de exibição .

Adicione um controle para o nome de usuário criando e inicializando um UITextField e, em seguida, adicionando-o à hierarquia de exibição, conforme mostrado abaixo:

class CustomViewController : UIViewController
{
    UITextField usernameField;

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        View.BackgroundColor = UIColor.Gray;

        nfloat h = 31.0f;
        nfloat w = View.Bounds.Width;

        usernameField = new UITextField
        {
            Placeholder = "Enter your username",
            BorderStyle = UITextBorderStyle.RoundedRect,
            Frame = new CGRect(10, 82, w - 20, h)
        };

        View.AddSubview(usernameField);
    }
}

Quando criamos o UITextField, definimos a Frame propriedade para definir sua localização e tamanho. No iOS, a coordenada 0,0 está no canto superior esquerdo com +x à direita e +y para baixo. Depois de definir o Frame junto com algumas outras propriedades, chamamos View.AddSubview para adicionar o UITextField à hierarquia de exibição. Isso torna a usernameField subexibição da UIView instância à qual a View propriedade faz referência. Uma subexibição é adicionada com uma ordem z superior à exibição pai, portanto, ela aparece na frente da exibição pai na tela.

A aplicação com o incluído é mostrada UITextField abaixo:

O aplicativo com o UITextField incluído

Podemos adicionar um UITextField para a senha de forma semelhante, só que desta vez definimos a SecureTextEntry propriedade como true, como mostrado abaixo:

public class CustomViewController : UIViewController
{
    UITextField usernameField, passwordField;
    public override void ViewDidLoad()
    {
       // keep the code the username UITextField
        passwordField = new UITextField
        {
            Placeholder = "Enter your password",
            BorderStyle = UITextBorderStyle.RoundedRect,
            Frame = new CGRect(10, 114, w - 20, h),
            SecureTextEntry = true
        };

      View.AddSubview(usernameField);
      View.AddSubview(passwordField);
   }
}

A configuração SecureTextEntry = true oculta o texto inserido UITextField no pelo usuário, conforme mostrado abaixo:

A configuração de SecureTextEntry true oculta o texto inserido pelo usuário

Adicionando o botão

Em seguida, adicionaremos um botão para que o usuário possa enviar o nome de usuário e a senha. O botão é adicionado à hierarquia de exibição como qualquer outro controle, passando-o como um argumento para o método do modo de AddSubview exibição pai novamente.

O código a seguir adiciona o botão e registra um manipulador de eventos para o TouchUpInside evento:

var submitButton = UIButton.FromType (UIButtonType.RoundedRect);

submitButton.Frame = new CGRect (10, 170, w - 20, 44);
submitButton.SetTitle ("Submit", UIControlState.Normal);

submitButton.TouchUpInside += (sender, e) => {
    Console.WriteLine ("Submit button pressed");
};

View.AddSubview(submitButton);

Com isso em vigor, a tela de login agora aparece como mostrado abaixo:

A tela de login

Ao contrário das versões anteriores do iOS, o fundo do botão padrão é transparente. Alterar a propriedade do BackgroundColor botão altera isso:

submitButton.BackgroundColor = UIColor.White;

Isso resultará em um botão quadrado em vez do típico botão com bordas arredondadas. Para obter a borda arredondada, use o seguinte trecho:

submitButton.Layer.CornerRadius = 5f;

Com essas alterações, a exibição ficará assim:

Um exemplo de execução do modo de exibição

Adicionando vários modos de exibição à hierarquia de exibição

O iOS fornece um recurso para adicionar vários modos de exibição à hierarquia de exibição usando AddSubviewso .

View.AddSubviews(new UIView[] { usernameField, passwordField, submitButton });

Adicionando funcionalidade de botão

Quando um botão é clicado, seus usuários esperam que algo aconteça. Por exemplo, um alerta é mostrado ou a navegação é executada para outra tela.

Vamos adicionar algum código para enviar um segundo controlador de exibição para a pilha de navegação.

Primeiro, crie o segundo controlador de exibição:

var loginVC = new UIViewController () { Title = "Login Success!"};
loginVC.View.BackgroundColor = UIColor.Purple;

Em seguida, adicione a funcionalidade ao TouchUpInside evento:

submitButton.TouchUpInside += (sender, e) => {
                this.NavigationController.PushViewController (loginVC, true);
            };

A navegação é ilustrada abaixo:

A navegação é ilustrada neste gráfico

Observe que, por padrão, quando você usa um Controlador de Navegação, o iOS fornece ao aplicativo uma barra de navegação e um botão Voltar para permitir que você volte pela pilha.

Iterando através da hierarquia de exibição

É possível iterar através da hierarquia de subvisualização e escolher qualquer exibição específica. Por exemplo, para localizar cada UIButton um e dar a esse botão um diferente BackgroundColor, o seguinte trecho pode ser usado

foreach(var subview in View.Subviews)
{
    if (subview is UIButton)
    {
         var btn = subview as UIButton;
         btn.BackgroundColor = UIColor.Green;
    }
}

Isso, no entanto, não funcionará se o modo de exibição que está sendo iterado for um UIView como todos os modos de exibição voltarão como sendo um como os UIView objetos adicionados ao modo de exibição pai herdarem UIView.

Rotação de manuseio

Se o usuário girar o dispositivo para paisagem, os controles não serão redimensionados adequadamente, como ilustra a captura de tela a seguir:

Se o usuário girar o dispositivo para paisagem, os controles não serão redimensionados adequadamente

Uma maneira de corrigir isso é definindo a AutoresizingMask propriedade em cada modo de exibição. Neste caso, queremos que os controles se estendam horizontalmente, então definiríamos cada AutoresizingMask. O exemplo a seguir é para usernameField, mas o mesmo precisaria ser aplicado a cada gadget na hierarquia de exibição.

usernameField.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;

Agora, quando giramos o dispositivo ou simulador, tudo se estende para preencher o espaço adicional, como mostrado abaixo:

Todos os controles se esticam para preencher o espaço adicional

Criando modos de exibição personalizados

Além de usar controles que fazem parte do UIKit, modos de exibição personalizados também podem ser usados. Um modo de exibição personalizado pode ser criado herdando e UIView substituindo Drawo . Vamos criar um modo de exibição personalizado e adicioná-lo à hierarquia de exibição para demonstrar.

Herdando de UIView

A primeira coisa que precisamos fazer é criar uma classe para o modo de exibição personalizado. Faremos isso usando o modelo Classe no Visual Studio para adicionar uma classe vazia chamada CircleView. A classe base deve ser definida como UIView, que lembramos estar no UIKit namespace. Também precisaremos do System.Drawing namespace. Os outros vários System.* namespaces não serão usados neste exemplo, portanto, sinta-se à vontade para removê-los.

A classe deve ter esta aparência:

using System;

namespace CodeOnlyDemo
{
    class CircleView : UIView
    {
    }
}

Desenhando em um UIView

Cada UIView um tem um Draw método que é chamado pelo sistema quando precisa ser desenhado. Draw nunca deve ser chamado diretamente. Ele é chamado pelo sistema durante o processamento do loop de execução. Na primeira vez que um modo de exibição for adicionado à hierarquia de exibição, seu Draw método será chamado. Chamadas subsequentes a Draw ocorrer quando o modo de exibição é marcado como precisando ser desenhado chamando um SetNeedsDisplay ou SetNeedsDisplayInRect no modo de exibição.

Podemos adicionar código de desenho à nossa exibição adicionando esse código dentro do método substituído Draw , conforme mostrado abaixo:

public override void Draw(CGRect rect)
{
    base.Draw(rect);

    //get graphics context
    using (var g = UIGraphics.GetCurrentContext())
    {
        // set up drawing attributes
        g.SetLineWidth(10.0f);
        UIColor.Green.SetFill();
        UIColor.Blue.SetStroke();

        // create geometry
        var path = new CGPath();
        path.AddArc(Bounds.GetMidX(), Bounds.GetMidY(), 50f, 0, 2.0f * (float)Math.PI, true);

        // add geometry to graphics context and draw
        g.AddPath(path);
        g.DrawPath(CGPathDrawingMode.FillStroke);
    }
}

Uma vez que CircleView é um UIView, também podemos definir UIView propriedades. Por exemplo, podemos definir o BackgroundColor no construtor:

public CircleView()
{
    BackgroundColor = UIColor.White;
}

Para usar o CircleView que acabamos de criar, podemos adicioná-lo como uma subexibição à hierarquia de exibição em um controlador existente, como fizemos com o UILabels e UIButton anteriormente, ou podemos carregá-lo como a exibição de um novo controlador. Vamos fazer o último.

Carregando um modo de exibição

UIViewController tem um método chamado LoadView que é chamado pelo controlador para criar sua exibição. Este é um local apropriado para criar uma exibição e atribuí-la à propriedade do View controlador.

Primeiro, precisamos de um controlador, então crie uma nova classe vazia chamada CircleController.

Adicione CircleController o seguinte código para definir o View como a CircleView (você não deve chamar a base implementação em sua substituição):

using UIKit;

namespace CodeOnlyDemo
{
    class CircleController : UIViewController
    {
        CircleView view;

        public override void LoadView()
        {
            view = new CircleView();
            View = view;
        }
    }
}

Finalmente, precisamos apresentar o controlador em tempo de execução. Vamos fazer isso adicionando um manipulador de eventos no botão de envio que adicionamos anteriormente, da seguinte maneira:

submitButton.TouchUpInside += delegate
{
    Console.WriteLine("Submit button clicked");

    //circleController is declared as class variable
    circleController = new CircleController();
    PresentViewController(circleController, true, null);
};

Agora, quando executamos o aplicativo e tocamos no botão enviar, a nova exibição com um círculo é exibida:

O novo modo de exibição com um círculo é exibido

Criando uma tela de inicialização

Uma tela de inicialização é exibida quando o aplicativo é iniciado como uma maneira de exibir aos usuários que ele é responsivo. Como uma tela de inicialização é exibida quando o aplicativo está sendo carregado, ela não pode ser criada em código, pois o aplicativo ainda está sendo carregado na memória.

Quando você cria um projeto iOS no Visual Studio, uma tela de inicialização é fornecida para você na forma de um arquivo .xib, que pode ser encontrado na pasta Recursos dentro do seu projeto.

Isso pode ser editado clicando duas vezes nele e abrindo-o no Xcode Interface Builder.

A Apple recomenda que um arquivo .xib ou Storyboard seja usado para aplicativos direcionados ao iOS 8 ou posterior, Quando você inicia qualquer arquivo no Xcode Interface Builder, você pode usar Classes de tamanho e layout automático para adaptar seu layout para que ele tenha uma boa aparência e seja exibido corretamente para todos os tamanhos de dispositivo. Uma imagem de inicialização estática pode ser usada além de um .xib ou Storyboard para permitir suporte para aplicativos direcionados a versões anteriores.

Para obter mais informações sobre como criar uma tela de inicialização, consulte os documentos abaixo:

Importante

A partir do iOS 9, a Apple recomenda que os Storyboards sejam usados como o principal método de criação de uma tela de inicialização.

Criando uma imagem de inicialização para aplicativos anteriores ao iOS 8

Uma imagem estática pode ser usada além de uma tela de inicialização .xib ou Storyboard se o aplicativo tiver como alvo versões anteriores ao iOS 8.

Essa imagem estática pode ser definida no arquivo Info.plist ou como um Catálogo de Ativos (para iOS 7) em seu aplicativo. Você precisará fornecer imagens separadas para cada tamanho de dispositivo (320x480, 640x960, 640x1136) em que seu aplicativo pode ser executado. Para obter mais informações sobre tamanhos de tela de inicialização, consulte o guia Imagens de tela de inicialização.

Importante

Se o seu aplicativo não tiver a Tela de Inicialização, você poderá notar que ele não se encaixa totalmente na tela. Se esse for o caso, certifique-se de incluir, pelo menos, uma imagem 640x1136 nomeada Default-568@2x.png para seu Info.plist.

Resumo

Este artigo discutiu como desenvolver aplicativos iOS programaticamente no Visual Studio. Analisamos como criar um projeto a partir de um modelo de projeto vazio, discutindo como criar e adicionar um controlador de exibição raiz à janela. Em seguida, mostramos como usar controles do UIKit para criar uma hierarquia de exibição dentro de um controlador para desenvolver uma tela de aplicativo. Em seguida, examinamos como fazer com que as exibições sejam dispostas adequadamente em diferentes orientações e vimos como criar uma exibição personalizada subclassificando UIView, bem como carregar a exibição em um controlador. Finalmente, exploramos como adicionar uma tela de inicialização a um aplicativo.