Partilhar via


Storyboards unificados no Xamarin.iOS

O iOS 8 inclui um novo mecanismo mais simples de usar para criar a interface do usuário — o storyboard unificado. Com um único storyboard para cobrir todos os diferentes tamanhos de tela de hardware, visualizações rápidas e responsivas podem ser criadas em um estilo "design-once, use-many".

Como o desenvolvedor não precisa mais criar um storyboard separado e específico para dispositivos iPhone e iPad, ele tem a flexibilidade de projetar aplicativos com uma interface comum e, em seguida, personalizar essa interface para diferentes classes de tamanho. Dessa forma, um aplicativo pode ser adaptado aos pontos fortes de cada fator de forma e cada interface de usuário pode ser ajustada para fornecer a melhor experiência.

Classes de tamanho

Antes do iOS 8, o desenvolvedor usava UIInterfaceOrientation e UIInterfaceIdiom diferenciava entre os modos retrato e paisagem, e entre dispositivos iPhone e iPad. No iOS8, a orientação e o dispositivo são determinados usando classes de tamanho.

Os dispositivos são definidos por classes de tamanho, em eixos verticais e horizontais, e há dois tipos de classes de tamanho no iOS 8:

  • Regular – Isso é para um tamanho de tela grande (como um iPad) ou um gadget que dá a impressão de um tamanho grande (como um UIScrollView
  • Compacto – Isso é para dispositivos menores (como um iPhone). Esse tamanho leva em conta a orientação do aparelho.

Se os dois conceitos forem usados juntos, o resultado será uma grade 2 x 2 que define os diferentes tamanhos possíveis que podem ser usados em ambas as diferentes orientações, como visto no diagrama a seguir:

Uma grade 2 x 2 que define os diferentes tamanhos possíveis que podem ser usados nas orientações Regular e Compacta

O desenvolvedor pode criar um View Controller que usa qualquer uma das quatro possibilidades que resultariam em layouts diferentes (como visto nos gráficos acima).

Classes de tamanho do iPad

O iPad, devido ao tamanho, tem um tamanho de classe regular para ambas as orientações.

Classes de tamanho do iPad

Classes de tamanho do iPhone

O iPhone tem diferentes classes de tamanho com base na orientação do dispositivo:

Classes de tamanho do iPhone

  • Quando o dispositivo está no modo retrato, a tela tem uma classe compacta horizontalmente e regular verticalmente
  • Quando o dispositivo está no modo paisagem, as classes de tela são invertidas do modo retrato.

iPhone 6 Plus Size Classes

Os tamanhos são os mesmos dos iPhones anteriores quando na orientação retrato, mas diferentes na paisagem:

iPhone 6 Plus Size Classes

Como o iPhone 6 Plus tem uma tela grande o suficiente, ele é capaz de ter uma classe de tamanho de largura regular no modo paisagem.

Suporte para uma nova escala de tela

O iPhone 6 Plus usa uma nova tela Retina HD com um fator de escala de tela de 3.0 (três vezes a resolução da tela original do iPhone). Para fornecer a melhor experiência possível nesses dispositivos, inclua um novo trabalho artístico projetado para essa escala de tela. No Xcode 6 e superior, os catálogos de ativos podem incluir imagens nos tamanhos 1x, 2x e 3x; basta adicionar os novos ativos de imagem e o iOS escolherá os ativos corretos ao executar em um iPhone 6 Plus.

O comportamento de carregamento de imagem no iOS também reconhece um sufixo @3x em arquivos de imagem. Por exemplo, se o desenvolvedor incluir um ativo de imagem (em resoluções diferentes) no pacote do aplicativo com os seguintes nomes de arquivo: MonkeyIcon.png, MonkeyIcon@2x.pnge MonkeyIcon@3x.png. No iPhone 6 Plus, a MonkeyIcon@3x.png imagem será usada automaticamente se o desenvolvedor carregar uma imagem usando o seguinte código:

UIImage icon = UIImage.FromFile("MonkeyImage.png");

Telas de Lançamento Dinâmico

O arquivo da tela de inicialização é exibido como uma tela inicial enquanto um aplicativo iOS é iniciado para fornecer feedback ao usuário de que o aplicativo está realmente iniciando. Antes do iOS 8, o desenvolvedor teria que incluir vários Default.png ativos de imagem para cada tipo de dispositivo, orientação e resolução de tela em que o aplicativo seria executado.

Novo no iOS 8, o desenvolvedor pode criar um único arquivo atômico .xib no Xcode que usa Auto Layout e Classes de Tamanho para criar uma Tela de Inicialização Dinâmica que funcionará para cada dispositivo, resolução e orientação. Isso não apenas reduz a quantidade de trabalho necessária do desenvolvedor para criar e manter todos os ativos de imagem necessários, mas também reduz o tamanho do pacote instalado do aplicativo.

Características

Traços são propriedades que podem ser usadas para determinar como um layout muda à medida que seu ambiente muda. Eles consistem em um conjunto de propriedades (o HorizontalSizeClass e VerticalSizeClass baseado em UIUserInterfaceSizeClass), bem como o idioma da interface ( UIUserInterfaceIdiom) e a escala de exibição.

Todos os estados acima são embrulhados em um contêiner que a Apple chama de Trait Collection ( UITraitCollection), que contém não apenas as propriedades, mas também seus valores.

Ambiente de características

Os ambientes de traços são uma nova interface no iOS 8 e podem retornar uma coleção de traços para os seguintes objetos:

  • Telas ( UIScreens ).
  • Janelas ( UIWindows ).
  • Exibir controladores ( UIViewController ).
  • Visualizações ( UIView ).
  • Controlador de Apresentação ( UIPresentationController ).

O desenvolvedor usa a Coleção de Traços retornada por um Ambiente de Características para determinar como uma interface do usuário deve ser definida.

Todos os ambientes de características fazem uma hierarquia como visto no diagrama a seguir:

O diagrama de hierarquia de Ambientes de Características

A Coleção de Traços que cada um dos Ambientes de Características acima possui fluirá, por padrão, do ambiente pai para o ambiente filho.

Além de obter a Coleção de Trait atual, o Ambiente de Trait tem um TraitCollectionDidChange método, que pode ser substituído nas subclasses View ou View Controller. O desenvolvedor pode usar esse método para modificar qualquer um dos elementos da interface do usuário que dependem de traços quando esses traços foram alterados.

Coleções de Traços Típicos

Esta seção abordará os tipos típicos de coleções de características que o usuário experimentará ao trabalhar com o iOS 8.

A seguir está uma coleção de características típica que o desenvolvedor pode ver em um iPhone:

Propriedade Valor
HorizontalSizeClass Compacto
VerticalSizeClass Regular
UserInterfaceIdom o Telefone
DisplayScale 2,0

O conjunto acima representaria uma Coleção de Traços Totalmente Qualificados, pois tem valores para todas as suas propriedades de traço.

Também é possível ter uma coleção de características que está faltando alguns de seus valores (que a Apple se refere como não especificado):

Propriedade Valor
HorizontalSizeClass Compacto
VerticalSizeClass Não Especificado
UserInterfaceIdom Não Especificado
DisplayScale Não Especificado

Geralmente, no entanto, quando o desenvolvedor solicita ao Trait Environment sua Trait Collection, ele retornará uma coleção totalmente qualificada, como visto no exemplo acima.

Se um Ambiente de Característica (como um View ou View Controller) não estiver dentro da hierarquia de exibição atual, o desenvolvedor poderá recuperar valores não especificados para uma ou mais das propriedades de traço.

O desenvolvedor também receberá uma Coleção de Trait parcialmente qualificada se usar um dos métodos de criação fornecidos pela Apple, como UITraitCollection.FromHorizontalSizeClass, para criar uma nova coleção.

Uma operação que pode ser executada em várias Coleções de Traços é compará-las entre si, o que envolve perguntar a uma Coleção de Traços se ela contém outra. O que se entende por Contenção é que, para qualquer característica especificada na segunda coleção, o valor deve corresponder exatamente ao valor na primeira coleção.

Para testar duas características use o Contains método da UITraitCollection passagem no valor da característica a ser testada.

O desenvolvedor pode executar as comparações manualmente no código para determinar como layout Views ou View Controllers. No entanto, UIKit usa esse método internamente para fornecer algumas de suas funcionalidades, como no Proxy de Aparência, por exemplo.

Proxy de aparência

O Proxy de Aparência foi introduzido em versões anteriores do iOS para permitir que os desenvolvedores personalizem as propriedades de suas Exibições. Ele foi estendido no iOS 8 para suportar Trait Collections.

Os Proxies de Aparência agora incluem um novo método, AppearanceForTraitCollection, que retorna um novo Proxy de Aparência para a determinada Coleção de Traços que foi passada. Quaisquer personalizações que o desenvolvedor executar nesse Proxy de Aparência só terão efeito em Exibições que estejam em conformidade com a Coleção de Traços especificada.

Geralmente, o desenvolvedor passará uma Coleção de Traços parcialmente especificada para o AppearanceForTraitCollection método, como um que acabou de especificar uma Classe de Tamanho Horizontal de Compacto, para que eles possam personalizar qualquer exibição no aplicativo que seja compacta horizontalmente.

UIImage

Outra classe à qual a Apple adicionou Trait Collection é UIImage. No passado, o desenvolvedor tinha que especificar uma versão @1X e @2x de qualquer ativo gráfico bitmap que eles iriam incluir no aplicativo (como um ícone).

O iOS 8 foi expandido para permitir que o desenvolvedor inclua várias versões de uma imagem em um catálogo de imagens com base em uma coleção de traços. Por exemplo, o desenvolvedor pode incluir uma imagem menor para trabalhar com uma classe de traço compacta e uma imagem em tamanho real para qualquer outra coleção.

Quando uma das imagens é usada dentro de uma UIImageView classe, a Exibição de Imagem exibirá automaticamente a versão correta da imagem para sua Coleção de Traços. Se o Ambiente de Características for alterado (como o usuário alternar o dispositivo de retrato para paisagem), o Modo de Exibição de Imagem selecionará automaticamente o novo tamanho da imagem para corresponder à nova Coleção de Traços e alterará seu tamanho para corresponder ao da versão atual da imagem que está sendo exibida.

UIImageAsset

A Apple adicionou uma nova classe ao iOS 8 chamada UIImageAsset para dar ao desenvolvedor ainda mais controle sobre a seleção de imagens.

Um Ativo de Imagem encerra todas as diferentes versões de uma imagem e permite que o desenvolvedor solicite uma imagem específica que corresponda a uma Coleção de Características que foi passada. As imagens podem ser adicionadas ou removidas de um Ativo de Imagem, em tempo real.

Para obter mais informações sobre ativos de imagem, consulte a documentação UIImageAsset da Apple.

Combinando coleções de traços

Outra função que um desenvolvedor pode executar em Coleções de Traços é adicionar duas juntas que resultarão na coleção combinada, onde os valores não especificados de uma coleção são substituídos pelos valores especificados em uma segunda. Isso é feito usando o FromTraitsFromCollectionsUITraitCollection método da classe.

Como dito acima, se qualquer um dos traços não for especificado em uma das Coleções de Traços e for especificado em outra, o valor será definido para a versão especificada. No entanto, se houver várias versões de um determinado valor especificado, o valor da última Coleção de Trait será o valor usado.

Controladores de visualização adaptável

Esta seção abordará os detalhes de como os controladores de exibição e exibição do iOS adotaram os conceitos de Traços e Classes de Tamanho para serem automaticamente mais adaptáveis nos aplicativos do desenvolvedor.

Controlador de Visualização Dividida

Uma das classes do View Controller que mais mudou no iOS 8 é a UISplitViewController classe. No passado, o desenvolvedor costumava usar um Split View Controller na versão iPad do aplicativo e, em seguida, eles teriam que fornecer uma versão completamente diferente de sua hierarquia de visualização para a versão iPhone do aplicativo.

No iOS 8, a UISplitViewController classe está disponível em ambas as plataformas (iPad e iPhone), o que permite ao desenvolvedor criar uma hierarquia View Controller que funcionará para iPhone e iPad.

Quando um iPhone está em paisagem, o controle Split View apresentará suas visualizações lado a lado, assim como faria ao ser exibido em um iPad.

Características Prevalecentes

Os ambientes de traço são em cascata do contêiner pai para os contêineres filho, como no gráfico a seguir mostrando um controlador de visualização dividida em um iPad na orientação paisagem:

Um controlador Split View em um iPad na orientação paisagem

Como o iPad tem uma Classe de Tamanho Normal nas orientações horizontal e vertical, o Split View exibirá as visualizações mestre e detalhada.

Em um iPhone, onde a classe de tamanho é compacta em ambas as orientações, o controlador Split View exibe apenas a visualização de detalhes, como visto abaixo:

O Controlador de Visualização Dividida exibe apenas o modo de exibição de detalhes

Em um aplicativo em que o desenvolvedor deseja exibir a exibição mestre e de detalhes em um iPhone na orientação paisagem, o desenvolvedor deve inserir um contêiner pai para o Split View Controller e substituir a Trait Collection. Como pode ser visto no gráfico abaixo:

O desenvolvedor deve inserir um contêiner pai para o Split View Controller e substituir a Trait Collection

A UIView é definido como o pai do Controlador de Exibição Dividida e o SetOverrideTraitCollection método é chamado na exibição passando em uma nova Coleção de Características e direcionando o Controlador de Exibição Dividida. A nova Coleção de Traços substitui o HorizontalSizeClass, definindo-o como Regular, para que o Controlador de Visualização Dividida exiba as visualizações mestre e de detalhes em um iPhone na orientação paisagem.

Observe que o VerticalSizeClass foi definido como unspecified, o que permite que a nova Coleção de Traços seja adicionada à Coleção de Traços no pai, resultando em um Compact VerticalSizeClass para o Controlador de Exibição Dividida filho.

Mudanças de traços

Esta seção examinará, em detalhes, como as Coleções de Traços fazem a transição quando o Ambiente de Características é alterado. Por exemplo, quando o dispositivo é girado de retrato para paisagem.

Visão geral do retrato à paisagem Alterações de traços

Primeiro, o iOS 8 faz algumas configurações para se preparar para a transição acontecer. Em seguida, o sistema anima o estado de transição. Finalmente, o iOS 8 limpa todos os estados temporários necessários durante a transição.

O iOS 8 fornece vários retornos de chamada que o desenvolvedor pode usar para participar da Alteração de Traço, conforme mostrado na tabela a seguir:

Fase Retorno de Chamada Descrição
Instalação
  • WillTransitionToTraitCollection
  • TraitCollectionDidChange
  • Esse método é chamado no início de uma Alteração de Traço antes que uma Coleção de Características seja definida como seu novo valor.
  • O método é chamado quando o valor da Trait Collection é alterado, mas antes de qualquer animação ocorrer.
Animação WillTransitionToTraitCollection O Coordenador de Transição que é passado para esse método tem uma AnimateAlongside propriedade que permite ao desenvolvedor adicionar animações que serão executadas junto com as animações padrão.
Limpar WillTransitionToTraitCollection Fornece um método para os desenvolvedores incluírem seu próprio código de limpeza após a transição.

O WillTransitionToTraitCollection método é ótimo para animar controladores de exibição junto com as alterações da coleção de traços. O WillTransitionToTraitCollection método só está disponível em controladores de exibição ( UIViewController) e não em outros ambientes de traço, como UIViews.

O TraitCollectionDidChange é ótimo para trabalhar com a UIView classe, onde o desenvolvedor deseja atualizar a interface do usuário à medida que as características estão mudando.

Recolhendo os controladores de visualização dividida

Agora vamos dar uma olhada mais de perto no que acontece quando um controlador de exibição dividida é recolhido de uma exibição de duas colunas para uma exibição de uma coluna. Como parte dessa mudança, há dois processos que precisam ocorrer:

  • Por padrão, o Controlador de Exibição Dividida usará o controlador de exibição primário como o modo de exibição após o recolhimento. O desenvolvedor pode substituir esse comportamento substituindo o GetPrimaryViewControllerForCollapsingSplitViewController método do e fornecendo qualquer controlador de UISplitViewControllerDelegate exibição que eles desejam exibir no estado recolhido.
  • O Controlador de Exibição Secundário precisa ser mesclado no Controlador de Exibição Primário. Geralmente o desenvolvedor não precisará tomar nenhuma ação para esta etapa; o Controlador Split View inclui o tratamento automático desta fase com base no dispositivo de hardware. No entanto, pode haver alguns casos especiais em que o desenvolvedor desejará interagir com essa alteração. Chamar o CollapseSecondViewController método do permite que o controlador de UISplitViewControllerDelegate exibição mestre seja exibido quando o colapso ocorre, em vez do modo de exibição de detalhes.

Expandindo o controlador Split View

Agora vamos dar uma olhada mais de perto no que acontece quando um controlador de exibição dividida é expandido de um estado recolhido. Mais uma vez, há duas etapas que precisam ocorrer:

  • Primeiro, defina o novo Controlador de Exibição Principal. Por padrão, o Controlador de Exibição Dividida usará automaticamente o Controlador de Exibição Principal do modo de exibição recolhido. Novamente, o desenvolvedor pode substituir esse comportamento usando o GetPrimaryViewControllerForExpandingSplitViewController método do UISplitViewControllerDelegate .
  • Depois que o Controlador de Exibição Primário tiver sido escolhido, o Controlador de Exibição Secundário deverá ser recriado. Novamente, o Controlador Split View inclui o manuseio automático dessa fase com base no dispositivo de hardware. O desenvolvedor pode substituir esse comportamento chamando o SeparateSecondaryViewControllerUISplitViewControllerDelegate método do .

Em um Controlador de Exibição Dividida, o Controlador de Exibição Primária desempenha um papel na expansão e no recolhimento das exibições implementando os CollapseSecondViewControllerUISplitViewControllerDelegatemétodos e SeparateSecondaryViewController do . UINavigationController implementa esses métodos para enviar e popar automaticamente o controlador de exibição secundária.

Mostrando controladores de exibição

Outra mudança que a Apple fez no iOS 8 está na forma como o desenvolvedor mostra os controles de visualização. No passado, se o aplicativo tivesse um Controlador de Exibição de Folha (como um Controlador de Exibição de Tabela) e o desenvolvedor mostrasse um diferente (por exemplo, em resposta ao toque do usuário em uma célula), o aplicativo voltaria através da hierarquia do controlador para o Controlador de Exibição de Navegação e chamaria o PushViewController método contra ele para exibir a nova exibição.

Isso apresentava um acoplamento muito estreito entre o Controlador de Navegação e o ambiente em que ele estava sendo executado. No iOS 8, a Apple desacoplou isso fornecendo dois novos métodos:

  • ShowViewController – Adapta-se para exibir o novo controlador de visualização com base em seu ambiente. Por exemplo, em um UINavigationController ele simplesmente empurra a nova exibição para a pilha. Em um Controlador de Visualização Dividida, o novo Controlador de Exibição será apresentado no lado esquerdo como o novo Controlador de Exibição Primária. Se nenhum controlador de exibição de contêiner estiver presente, o novo modo de exibição será exibido como um controlador de exibição modal.
  • ShowDetailViewController – Funciona de forma semelhante ao ShowViewController, mas é implementado em um Split View Controller para substituir a visualização de detalhes com o novo View Controller sendo passado. Se o Controlador de Visualização Dividida estiver recolhido (como pode ser visto em um Aplicativo do iPhone), a chamada será redirecionada para o ShowViewController método e a nova exibição será mostrada como o Controlador de Exibição Principal. Novamente, se nenhum controlador de exibição de contêiner estiver presente, o novo modo de exibição será exibido como um controlador de exibição modal.

Esses métodos funcionam iniciando no Leaf View Controller e subindo a hierarquia de exibição até encontrar o controlador de exibição de contêiner correto para manipular a exibição do novo modo de exibição.

Os desenvolvedores podem implementar ShowViewController e ShowDetailViewController em seus próprios controladores de exibição personalizados para obter a mesma funcionalidade automatizada que UINavigationController e UISplitViewController fornece.

Como ele funciona

Nesta seção, vamos dar uma olhada em como esses métodos são realmente implementados no iOS 8. Vejamos primeiro o novo GetTargetForAction método:

O novo método GetTargetForAction

Esse método percorre a cadeia de hierarquia até que o controlador de exibição de contêiner correto seja encontrado. Por exemplo:

  1. Se um ShowViewController método for chamado, o primeiro View Controller na cadeia que implementa esse método será o Navigation Controller, portanto, ele será usado como o pai do novo modo de exibição.
  2. Se um ShowDetailViewController método foi chamado em vez disso, o Controlador de Exibição Dividida é o primeiro Controlador de Exibição a implementá-lo, portanto, ele é usado como o pai.

O GetTargetForAction método funciona localizando um View Controller que implementa uma determinada Action e, em seguida, perguntando a esse View Controller se ele deseja receber essa ação. Como esse método é público, os desenvolvedores podem criar seus próprios métodos personalizados que funcionam exatamente como os métodos internos ShowViewController e ShowDetailViewController internos.

Apresentação adaptativa

No iOS 8, a Apple também tornou o Popover Presentations ( UIPopoverPresentationController) adaptável. Assim, um Popover Presentation View Controller apresentará automaticamente uma Popover View normal em uma Classe de Tamanho Regular, mas a exibirá em tela cheia em uma Classe de Tamanho horizontalmente compacta (como em um iPhone).

Para acomodar as alterações no sistema de storyboard unificado, um novo objeto de controlador foi criado para gerenciar os controladores de exibição apresentados — UIPresentationController. Esse controlador é criado a partir do momento em que o controlador de exibição é apresentado até que ele seja descartado. Como é uma classe de gerenciamento, ela pode ser considerada uma superclasse sobre o View Controller, pois responde a alterações de dispositivo que afetam o View Controller (como orientação), que são então alimentadas de volta para o View Controller, os controles do Presentation Controller.

Quando o desenvolvedor apresenta um View Controller usando o PresentViewController método, o gerenciamento do processo de apresentação é entregue ao UIKit. UIKit manipula (entre outras coisas) o controlador correto para o estilo que está sendo criado, com a única exceção sendo quando um View Controller tem o estilo definido como UIModalPresentationCustom. Aqui, o aplicativo pode fornecer seu próprio PresentationController em vez de usar o UIKit controlador.

Estilos de apresentação personalizados

Com um estilo de apresentação personalizado, os desenvolvedores têm a opção de usar um Controlador de Apresentação personalizado. Esse controlador personalizado pode ser usado para modificar a aparência e o comportamento do Modo de Exibição ao qual ele é aliado.

Trabalhando com classes de tamanho

O Projeto Xamarin de Fotos Adaptáveis incluído neste artigo fornece um exemplo funcional de uso de Classes de Tamanho e Controladores de Exibição Adaptável em um aplicativo de Interface Unificada do iOS 8.

Enquanto o aplicativo cria sua interface do usuário completamente a partir do código, em vez de criar um Unified Storyboard usando o Construtor de Interface do Xcode, as mesmas técnicas se aplicam.

Agora vamos dar uma olhada mais de perto em como o projeto Adaptive Photos está implementando vários dos recursos Size Class no iOS 8 para criar um aplicativo adaptável.

Adaptando-se às mudanças no ambiente de características

Ao executar o aplicativo Fotos Adaptáveis em um iPhone, quando o usuário girar o dispositivo de retrato para paisagem, o Controlador de Visualização Dividida exibirá a exibição mestre e de detalhes:

O Split View Controller exibirá a visualização mestre e de detalhes, como visto aqui

Isso é feito substituindo o UpdateConstraintsForTraitCollection método do View Controller e ajustando as restrições com base no valor do VerticalSizeClass. Por exemplo:

public void UpdateConstraintsForTraitCollection (UITraitCollection collection)
{
    var views = NSDictionary.FromObjectsAndKeys (
        new object[] { TopLayoutGuide, ImageView, NameLabel, ConversationsLabel, PhotosLabel },
        new object[] { "topLayoutGuide", "imageView", "nameLabel", "conversationsLabel", "photosLabel" }
    );

    var newConstraints = new List<NSLayoutConstraint> ();
    if (collection.VerticalSizeClass == UIUserInterfaceSizeClass.Compact) {
        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|[imageView]-[nameLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("[imageView]-[conversationsLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("[imageView]-[photosLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:|[topLayoutGuide]-[nameLabel]-[conversationsLabel]-[photosLabel]",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:|[topLayoutGuide][imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.Add (NSLayoutConstraint.Create (ImageView, NSLayoutAttribute.Width, NSLayoutRelation.Equal,
            View, NSLayoutAttribute.Width, 0.5f, 0.0f));
    } else {
        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|[imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[nameLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[conversationsLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[photosLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:[topLayoutGuide]-[nameLabel]-[conversationsLabel]-[photosLabel]-20-[imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));
    }

    if (constraints != null)
        View.RemoveConstraints (constraints.ToArray ());

    constraints = newConstraints;
    View.AddConstraints (constraints.ToArray ());
}

Adicionando animações de transição

Quando o Controlador de Exibição Dividida no aplicativo Fotos Adaptáveis passa de recolhido para expandido, as animações são adicionadas às animações padrão substituindo o WillTransitionToTraitCollection método do controlador de exibição. Por exemplo:

public override void WillTransitionToTraitCollection (UITraitCollection traitCollection, IUIViewControllerTransitionCoordinator coordinator)
{
    base.WillTransitionToTraitCollection (traitCollection, coordinator);
    coordinator.AnimateAlongsideTransition ((UIViewControllerTransitionCoordinatorContext) => {
        UpdateConstraintsForTraitCollection (traitCollection);
        View.SetNeedsLayout ();
    }, (UIViewControllerTransitionCoordinatorContext) => {
    });
}

Substituindo o ambiente de características

Como mostrado acima, o aplicativo Fotos Adaptáveis força o Controlador Split View a exibir os detalhes e as visualizações mestras quando o dispositivo iPhone estiver na visualização paisagem.

Isso foi feito usando o seguinte código no controlador de exibição:

private UITraitCollection forcedTraitCollection = new UITraitCollection ();
...

public UITraitCollection ForcedTraitCollection {
    get {
        return forcedTraitCollection;
    }

    set {
        if (value != forcedTraitCollection) {
            forcedTraitCollection = value;
            UpdateForcedTraitCollection ();
        }
    }
}
...

public override void ViewWillTransitionToSize (SizeF toSize, IUIViewControllerTransitionCoordinator coordinator)
{
    ForcedTraitCollection = toSize.Width > 320.0f ?
         UITraitCollection.FromHorizontalSizeClass (UIUserInterfaceSizeClass.Regular) :
         new UITraitCollection ();

    base.ViewWillTransitionToSize (toSize, coordinator);
}

public void UpdateForcedTraitCollection ()
{
    SetOverrideTraitCollection (forcedTraitCollection, viewController);
}

Expandindo e recolhendo o controlador de visualização dividida

Em seguida, vamos examinar como o comportamento de expansão e colapso do Split View Controller foi implementado no Xamarin. AppDelegateNo , quando o Controlador de Exibição Dividida é criado, seu representante é atribuído para lidar com essas alterações:

public class SplitViewControllerDelegate : UISplitViewControllerDelegate
{
    public override bool CollapseSecondViewController (UISplitViewController splitViewController,
        UIViewController secondaryViewController, UIViewController primaryViewController)
    {
        AAPLPhoto photo = ((CustomViewController)secondaryViewController).Aapl_containedPhoto (null);
        if (photo == null) {
            return true;
        }

        if (primaryViewController.GetType () == typeof(CustomNavigationController)) {
            var viewControllers = new List<UIViewController> ();
            foreach (var controller in ((UINavigationController)primaryViewController).ViewControllers) {
                var type = controller.GetType ();
                MethodInfo method = type.GetMethod ("Aapl_containsPhoto");

                if ((bool)method.Invoke (controller, new object[] { null })) {
                    viewControllers.Add (controller);
                }
            }

            ((UINavigationController)primaryViewController).ViewControllers = viewControllers.ToArray<UIViewController> ();
        }

        return false;
    }

    public override UIViewController SeparateSecondaryViewController (UISplitViewController splitViewController,
        UIViewController primaryViewController)
    {
        if (primaryViewController.GetType () == typeof(CustomNavigationController)) {
            foreach (var controller in ((CustomNavigationController)primaryViewController).ViewControllers) {
                var type = controller.GetType ();
                MethodInfo method = type.GetMethod ("Aapl_containedPhoto");

                if (method.Invoke (controller, new object[] { null }) != null) {
                    return null;
                }
            }
        }

        return new AAPLEmptyViewController ();
    }
}

O SeparateSecondaryViewController método testa para ver se uma foto está sendo exibida e executa ações com base nesse estado. Se nenhuma foto estiver sendo mostrada, ela recolherá o Controlador de Exibição Secundário para que o Controlador de Exibição Mestre seja exibido.

O CollapseSecondViewController método é usado ao expandir o Split View Controller para ver se existem fotos na pilha, se assim for, ele volta para essa exibição.

Movendo-se entre controladores de exibição

Em seguida, vamos dar uma olhada em como o aplicativo Adaptive Photos se move entre os controladores de exibição. AAPLConversationViewController Na classe quando o usuário seleciona uma célula da tabela, o ShowDetailViewController método é chamado para exibir a exibição de detalhes:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    var photo = PhotoForIndexPath (indexPath);
    var controller = new AAPLPhotoViewController ();
    controller.Photo = photo;

    int photoNumber = indexPath.Row + 1;
    int photoCount = (int)Conversation.Photos.Count;
    controller.Title = string.Format ("{0} of {1}", photoNumber, photoCount);
    ShowDetailViewController (controller, this);
}

Exibição de Indicadores de Divulgação

No aplicativo Foto Adaptativa há vários lugares onde os Indicadores de Divulgação são ocultos ou mostrados com base em alterações no Ambiente de Traço. Isso é tratado com o seguinte código:

public bool Aapl_willShowingViewControllerPushWithSender ()
{
    var selector = new Selector ("Aapl_willShowingViewControllerPushWithSender");
    var target = this.GetTargetViewControllerForAction (selector, this);

    if (target != null) {
        var type = target.GetType ();
        MethodInfo method = type.GetMethod ("Aapl_willShowingDetailViewControllerPushWithSender");
        return (bool)method.Invoke (target, new object[] { });
    } else {
        return false;
    }
}

public bool Aapl_willShowingDetailViewControllerPushWithSender ()
{
    var selector = new Selector ("Aapl_willShowingDetailViewControllerPushWithSender");
    var target = this.GetTargetViewControllerForAction (selector, this);

    if (target != null) {
        var type = target.GetType ();
        MethodInfo method = type.GetMethod ("Aapl_willShowingDetailViewControllerPushWithSender");
        return (bool)method.Invoke (target, new object[] { });
    } else {
        return false;
    }
}

Estes são implementados usando o método discutido GetTargetViewControllerForAction em detalhes acima.

Quando um Controlador de Exibição de Tabela está exibindo dados, ele usa os métodos implementados acima para ver se um push vai ou não acontecer e se deve ou não exibir ou ocultar o Indicador de Divulgação de acordo:

public override void WillDisplay (UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{
    bool pushes = ShouldShowConversationViewForIndexPath (indexPath) ?
         Aapl_willShowingViewControllerPushWithSender () :
         Aapl_willShowingDetailViewControllerPushWithSender ();

    cell.Accessory = pushes ? UITableViewCellAccessory.DisclosureIndicator : UITableViewCellAccessory.None;
    var conversation = ConversationForIndexPath (indexPath);
    cell.TextLabel.Text = conversation.Name;
}

Novo ShowDetailTargetDidChangeNotification tipo

A Apple adicionou um novo tipo de notificação para trabalhar com classes de tamanho e ambientes de características de dentro de um controlador Split View, ShowDetailTargetDidChangeNotification. Essa notificação é enviada sempre que o Modo de Exibição de Detalhes de destino para um Controlador de Exibição Dividida é alterado, como quando o controlador se expande ou colapsa.

O aplicativo Fotos Adaptáveis usa essa notificação para atualizar o estado do Indicador de Divulgação quando o Controlador de Exibição de Detalhes é alterado:

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();
    TableView.RegisterClassForCellReuse (typeof(UITableViewCell), AAPLListTableViewControllerCellIdentifier);
    NSNotificationCenter.DefaultCenter.AddObserver (this, new Selector ("showDetailTargetDidChange:"),
        UIViewController.ShowDetailTargetDidChangeNotification, null);
    ClearsSelectionOnViewWillAppear = false;
}

Dê uma olhada mais de perto no aplicativo Adaptive Photos para ver todas as maneiras pelas quais as classes de tamanho, as coleções de características e os controladores de exibição adaptáveis podem ser usados para criar facilmente um aplicativo unificado no Xamarin.iOS.

Storyboards unificadas

Novidade no iOS 8, os Storyboards Unificados permitem que o desenvolvedor crie um arquivo de storyboard unificado que pode ser exibido em dispositivos iPhone e iPad segmentando várias classes de tamanho. Usando Storyboards unificados, o desenvolvedor escreve menos código específico da interface do usuário e tem apenas um design de interface para criar e manter.

Os principais benefícios do Unified Storyboards são:

  • Use o mesmo arquivo de storyboard para iPhone e iPad.
  • Implante de forma retroativa no iOS 6 e iOS 7.
  • Visualize o layout para diferentes dispositivos, orientações e versões do sistema operacional.

Habilitando classes de tamanho

Por padrão, qualquer novo projeto Xamarin.iOS usará classes de tamanho. Para usar Classes de Tamanho e Sequências Adaptáveis dentro de um storyboard de um projeto mais antigo, ele deve primeiro ser convertido para o formato de Storyboard Unificado do Xcode 6 e a caixa de seleção Usar Classes de Tamanho está marcada no Inspetor de Arquivos do Xcode para seus storyboards.

Telas de Lançamento Dinâmico

O arquivo da tela de inicialização é exibido como uma tela inicial enquanto um aplicativo iOS é iniciado para fornecer feedback ao usuário de que o aplicativo está realmente iniciando. Antes do iOS 8, o desenvolvedor teria que incluir vários Default.png ativos de imagem para cada tipo de dispositivo, orientação e resolução de tela em que o aplicativo seria executado. Por exemplo, Default@2x.png, Default-Landscape@2x~ipad.png, , Default-Portrait@2x~ipad.pngetc.

Levando em consideração os novos dispositivos iPhone 6 e iPhone 6 Plus (e o próximo Apple Watch) com todos os dispositivos iPhone e iPad existentes, isso representa uma grande variedade de tamanhos, orientações e resoluções variadas de ativos de imagem da tela de Default.png inicialização que devem ser criados e mantidos. Além disso, esses arquivos podem ser bastante grandes e irão "inchar" o pacote de aplicativos entregue, aumentando a quantidade de tempo necessária para baixar o aplicativo da iTunes App Store (possivelmente impedindo-o de ser entregue através de uma rede celular) e aumentando a quantidade de armazenamento necessária no dispositivo do usuário final.

Novo no iOS 8, o desenvolvedor pode criar um único arquivo atômico .xib no Xcode que usa Auto Layout e Classes de Tamanho para criar uma Tela de Inicialização Dinâmica que funcionará para cada dispositivo, resolução e orientação. Isso não apenas reduz a quantidade de trabalho necessária do desenvolvedor para criar e manter todos os ativos de imagem necessários, mas também reduz consideravelmente o tamanho do pacote configurável instalado do aplicativo.

As Telas de Inicialização Dinâmica têm as seguintes limitações e considerações:

  • Use apenas UIKit classes.
  • Use um único modo de exibição raiz que seja um UIView objeto OR UIViewController .
  • Não faça conexões com o código do aplicativo (não adicione Ações ou Outlets).
  • Não adicione UIWebView objetos.
  • Não use classes personalizadas.
  • Não use atributos de tempo de execução.

Com as diretrizes acima em mente, vamos analisar a adição de uma tela de inicialização dinâmica a um projeto existente do Xamarin iOS 8.

Faça o seguinte:

  1. Abra o Visual Studio para Mac e carregue a Solução para adicionar a Tela de Início Dinâmico.

  2. No Gerenciador de Soluções, clique com o botão direito do mouse no MainStoryboard.storyboard arquivo e selecione Abrir com>o Xcode Interface Builder:

    Abrir com o Xcode Interface Builder

  3. No Xcode, selecione Arquivo>Novo>Arquivo...:

    Selecionar Arquivo / Novo

  4. Selecione iOS>User Interface>Launch Screen e clique no botão Next:

    Selecione iOS / Interface do usuário / Tela de inicialização

  5. Nomeie o arquivo LaunchScreen.xib e clique no botão Criar :

    Nomeie o arquivo LaunchScreen.xib

  6. Edite o design da tela de inicialização adicionando elementos gráficos e usando Restrições de layout para posicioná-los para os dispositivos, orientações e tamanhos de tela fornecidos:

    Editando o design da tela de inicialização

  7. Salve as alterações em LaunchScreen.xib.

  8. Selecione o Destino de aplicativos e a guia Geral :

    Selecione o Destino de aplicativos e a guia Geral

  9. Clique no botão Escolher Info.plist , selecione o Info.plist para o aplicativo Xamarin e clique no botão Escolher :

    Selecione o Info.plist para o aplicativo Xamarin

  10. Na seção Ícones do aplicativo e Iniciar imagens, abra o menu suspenso Arquivo da tela de inicialização e escolha o LaunchScreen.xib criado acima:

    Escolha o LaunchScreen.xib

  11. Salve as alterações no arquivo e retorne ao Visual Studio para Mac.

  12. Aguarde até que o Visual Studio para Mac conclua a sincronização das alterações com o Xcode.

  13. No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Recursos e selecione Adicionar>Arquivos...:

    Selecione Adicionar / Adicionar arquivos...

  14. Selecione o LaunchScreen.xib arquivo criado acima e clique no botão Abrir :

    Selecione o arquivo LaunchScreen.xib

  15. Construa o aplicativo.

Testando a tela de inicialização dinâmica

No Visual Studio para Mac, selecione o simulador Retina do iPhone 4 e execute o aplicativo. A Tela de Inicialização Dinâmica será exibida no formato e orientação corretos:

A tela de inicialização dinâmica exibida na orientação vertical

Pare o aplicativo no Visual Studio para Mac e selecione um dispositivo iPad iOS 8. Execute o aplicativo e a tela de inicialização será formatada corretamente para este dispositivo e orientação:

A tela de inicialização dinâmica exibida na orientação horizontal

Retorne ao Visual Studio para Mac e pare a execução do aplicativo.

Trabalhando com o iOS 7

Para manter a retrocompatibilidade com o iOS 7, basta incluir os ativos de imagem usuais Default.png normalmente no aplicativo iOS 8. O iOS retornará ao comportamento anterior e usará esses arquivos como a tela de inicialização ao ser executado em um dispositivo iOS 7.

Resumo

Este artigo deu uma olhada rápida nas classes de tamanho e como elas afetam o layout em dispositivos iPhone e iPad. Ele discutiu como Traits, Trait Environments e Trait Collections funcionam com Classes de Tamanho para criar Interfaces Unificadas. Ele analisou brevemente os Controladores de Exibição Adaptativa e como eles funcionam com Classes de Tamanho dentro das Interfaces Unificadas. Ele analisou a implementação de classes de tamanho e interfaces unificadas completamente a partir do código C# dentro de um aplicativo Xamarin iOS 8.

Finalmente, este artigo abordou o básico da criação de uma única tela de inicialização dinâmica que será exibida como a tela de inicialização em todos os dispositivos iOS 8.