Controles personalizados no Designer do Xamarin para iOS

O Xamarin Designer para iOS dá suporte à renderização de controles personalizados criados em seu projeto ou referenciados de fontes externas, como o Repositório de Componentes do Xamarin.

Aviso

O Designer do iOS foi preterido no Visual Studio 2019 versão 16.8 e Visual Studio 2019 para Mac versão 8.8 e removido no Visual Studio 2019 versão 16.9 e Visual Studio para Mac versão 8.9. A maneira recomendada de criar interfaces de usuário do iOS é diretamente em um Mac executando o Xcode. Para obter mais informações, consulte Criando interfaces do usuário com xcode.

O Designer do Xamarin para iOS é uma ferramenta poderosa para visualizar a interface do usuário de um aplicativo e fornece suporte à edição WYSIWYG para a maioria dos controladores de exibição e exibição do iOS. Seu aplicativo também pode conter controles personalizados que estendem os integrados ao iOS. Se esses controles personalizados forem escritos com algumas diretrizes em mente, eles também poderão ser renderizados pela Designer do iOS, fornecendo uma experiência de edição ainda mais rica. Este documento analisa essas diretrizes.

Requisitos

Um controle que atenda a todos os seguintes requisitos será renderizado na superfície de design:

  1. É uma subclasse direta ou indireta de UIView ou UIViewController. Outras subclasses NSObject aparecerão como um ícone na superfície de design.
  2. Ele tem um RegisterAttribute para expô-lo a Objective-C.
  3. Ele tem o construtor IntPtr necessário.
  4. Ele implementa a interface IComponent ou tem um DesignTimeVisibleAttribute definido como True.

Os controles definidos no código que atendem aos requisitos acima aparecerão no designer quando seu projeto de contenção for compilado para o simulador. Por padrão, todos os controles personalizados aparecerão na seção Componentes Personalizados da Caixa de Ferramentas. No entanto , o CategoryAttribute pode ser aplicado à classe do controle personalizado para especificar uma seção diferente.

O designer não dá suporte ao carregamento de bibliotecas de Objective-C terceiros.

Propriedades personalizadas

Uma propriedade declarada por um controle personalizado aparecerá no painel de propriedades se as seguintes condições forem atendidas:

  1. A propriedade tem um getter público e setter.
  2. A propriedade tem um ExportAttribute , bem como um BrowsableAttribute definido como True.
  3. O tipo de propriedade é um tipo numérico, tipo de enumeração, cadeia de caracteres, bool, SizeF, UIColor ou UIImage. Essa lista de tipos com suporte pode ser expandida no futuro.

A propriedade também pode ser decorada com um DisplayNameAttribute para especificar o rótulo exibido para ela no painel de propriedades.

Inicialização

Para UIViewController subclasses, você deve usar o método ViewDidLoad para código que depende das exibições criadas no designer.

Para UIView e outras NSObject subclasses, o método AwakeFromNib é o local recomendado para executar a inicialização do controle personalizado depois que ele é carregado do arquivo de layout. Isso ocorre porque todas as propriedades personalizadas definidas no painel de propriedades não serão definidas quando o construtor do controle for executado, mas elas serão definidas antes AwakeFromNib de ser chamado:

[Register ("CustomView"), DesignTimeVisible (true)]
public class CustomView : UIView {

    public CustomView (IntPtr handle) : base (handle) { }

    public override void AwakeFromNib ()
    {
        // Initialize the view here.
    }
}

Se o controle também for projetado para ser criado diretamente do código, talvez você queira criar um método que tenha um código de inicialização comum, desta forma:

[Register ("CustomView"), DesignTimeVisible (true)]
public class CustomView : UIView {

    public CustomView (IntPtr handle) : base (handle) { }

    public CustomView ()
    {
        // Called when created from code.
        Initialize ();
    }

    public override void AwakeFromNib ()
    {
        // Called when loaded from xib or storyboard.
        Initialize ();
    }

    void Initialize ()
    {
        // Common initialization code here.
    }
}

Inicialização de propriedade e AwakeFromNib

Deve-se tomar cuidado quando e onde inicializar propriedades projetáveis em um componente personalizado para não substituir valores que foram definidos dentro do Designer do iOS. Por exemplo, use o seguinte código:

[Register ("CustomView"), DesignTimeVisible (true)]
public class CustomView : UIView {

    [Export ("Counter"), Browsable (true)]
    public int Counter {get; set;}

    public CustomView (IntPtr handle) : base (handle) { }

    public CustomView ()
    {
        // Called when created from code.
        Initialize ();
    }

    public override void AwakeFromNib ()
    {
        // Called when loaded from xib or storyboard.
        Initialize ();
    }

    void Initialize ()
    {
        // Common initialization code here.
        Counter = 0;
    }
}

O CustomView componente expõe uma Counter propriedade que pode ser definida pelo desenvolvedor dentro do Designer do iOS. No entanto, independentemente do valor definido dentro do designer, o valor da Counter propriedade sempre será zero (0). Eis o motivo:

  • Uma instância do CustomControl é inflada do arquivo Storyboard.
  • Todas as propriedades modificadas no designer do iOS são definidas (como definir o valor de Counter como dois (2), por exemplo).
  • O AwakeFromNib método é executado e uma chamada é feita para o método do Initialize componente.
  • Dentro Initialize do valor da Counter propriedade está sendo redefinido para zero (0).

Para corrigir a situação acima, inicialize a Counter propriedade em outro lugar (como no construtor do componente) ou não substitua o AwakeFromNib método e chame Initialize se o componente não exigir nenhuma inicialização adicional fora do que está sendo tratado atualmente por seus construtores.

Modo Design

Na superfície de design, um controle personalizado deve seguir algumas restrições:

  • Os recursos do pacote de aplicativos não estão disponíveis no modo de design. As imagens estão disponíveis quando carregadas por meio dos métodos UIImage .
  • As operações assíncronas, como solicitações da Web, não devem ser executadas no modo de design. A superfície de design não dá suporte à animação nem a nenhuma outra atualização assíncrona à interface do usuário do controle.

Um controle personalizado pode implementar o IComponent e usar a propriedade DesignMode para marcar se estiver na superfície de design. Neste exemplo, o rótulo exibirá "Modo de Design" na superfície de design e "Runtime" em runtime:

[Register ("DesignerAwareLabel")]
public class DesignerAwareLabel : UILabel, IComponent {

    #region IComponent implementation

    public ISite Site { get; set; }
    public event EventHandler Disposed;

    #endregion

    public DesignerAwareLabel (IntPtr handle) : base (handle) { }

    public override void AwakeFromNib ()
    {
        if (Site != null && Site.DesignMode)
            Text = "Design Mode";
        else
            Text = "Runtime";
    }
}

Você deve sempre marcar a Site propriedade para null antes de tentar acessar qualquer um de seus membros. Se Site for , é nullseguro assumir que o controle não está em execução no designer. No modo de design, Site será definido depois que o construtor do controle tiver sido executado e antes AwakeFromNib de ser chamado.

Depuração

Um controle que atenda aos requisitos acima será exibido na caixa de ferramentas e renderizado na superfície. Se um controle não for renderizado, marcar para bugs no controle ou uma de suas dependências.

A superfície de design geralmente pode capturar exceções geradas por controles individuais enquanto continua a renderizar outros controles. O controle com falha é substituído por um espaço reservado vermelho e você pode exibir o rastreamento de exceção clicando no ícone de exclamação:

Um controle defeituoso como espaço reservado vermelho e os detalhes da exceção

Se os símbolos de depuração estiverem disponíveis para o controle, o rastreamento terá nomes de arquivo e números de linha. Clicar duas vezes em uma linha no rastreamento de pilha saltará para essa linha no código-fonte.

Se o designer não puder isolar o controle defeituoso, uma mensagem de aviso aparecerá na parte superior da superfície de design:

Uma mensagem de aviso na parte superior da superfície de design

A renderização completa será retomada quando o controle defeituoso for corrigido ou removido da superfície de design.

Resumo

Este artigo introduziu a criação e a aplicação de controles personalizados no designer do iOS. Primeiro, descreveu os requisitos que os controles devem atender para serem renderizados na superfície de design e expor propriedades personalizadas no painel de propriedades. Em seguida, ele examinou o código por trás – inicialização do controle e da propriedade DesignMode. Por fim, ele descreveu o que acontece quando exceções são lançadas e como resolve isso.