суббота, 28 апреля 2018 г.

Edgesforextendedlayout interface de controle da interface do usuário


Meu aplicativo para iPad abre o controlador de exibição restrita com o estilo de apresentação da página. Como você sabe, o estilo de apresentação de página não cobre a barra de status do controlador de exibição de apresentação para indicar a apresentação da página. A partir do controlador de visões modais, o aplicativo abre o UIImagePickerController para fazer uma foto. UIImagePickerController tem estilo de apresentação em tela cheia. Depois de dispensar o seletor de imagens, o controlador de exibição modal se torna 20px mais alto e se sobrepõe à barra de status do controlador de visualização inicial. Eu tentei substituir UIImagePickerController com simples UINavigationController e quebra meu controlador de modo restrito também. Eles única maneira de restaurar o tamanho do controlador de exibição de página está alterando a altura do quadro viewController. view. superview. superview. superview. superview depois de retornar ao controlador de exibição de página. Mas é realmente estranho. Existe outra maneira de corrigir a apresentação de controlador de exibição de página modal depois de dispensar o controlador de exibição restrita aninhado UPDATE: Eu usei esse código estranho para resolver o meu problema: Eu não acredito que não há solução mais elegante. Alguma idéia de como melhorá-lo UPDATE2: Acabei de abrir um bug. Você pode segui-lo: rdar: // 15949644 UPDATE3: Existe o meu projeto de amostra: link Sim, em nosso próprio aplicativo. Temos um controlador de exibição de folha de página modal apresentando um controlador de visualização de câmera em tela cheia, e experimentamos a mesma anomalia visual depois de dispensar. Consideramos um problema menor, por isso apenas abrimos um relatório de bug e não procuramos uma solução alternativa. Eu poderia dar uma olhada durante o fim de semana e ver se eu posso chegar a uma solução mais elegante, talvez em algum lugar no sistema de layout, mas não posso fazer promessas. ndash Leo Natan Jan 29 14 at 23:09 Assegure-se de que o ViewController que está apresentando a visão modal esteja em um NavigationController e essa estranheza deve parar. Minha resposta inicial - errado Atualizado Isso realmente soa como um bug, embora a partir de um ponto de vista da experiência do usuário você não deve realmente bater. Parece um pouco errado apresentar um controlador de exibição que está no estilo de apresentação de Página e, em seguida, apresentar outro sobre ele no modo de tela inteira. IMO isso é apenas design ruim desde o início, então o fato de que ele não funciona como você esperava, provavelmente, é porque quem configurá-lo não antecipou alguém usá-lo assim. Eu gostaria, bem como minha resposta inicial, apesar de breve, incorporar seu controlador de modo de exibição apresentado em um controlador de navegação e empurrar o UIImagPickerViewController para ele ou adicionar sua exibição animada à sua como se aparecesse como outro estilo de página. Visão. Se isso é desejado, afete. Nada disso parece perfeito, uma solução mais perfeita seria olhar para o fluxo do seu aplicativo e talvez reavaliar como as coisas são apresentadas. Definitivamente, há um problema com o UIImagePicker (ou mais amplamente, o UINavigationController) que não obedece a nenhuma configuração da barra de status no aplicativo. Isso foi discutido e resolvido reaplicando suas configurações de barra de status no delegado UINavigationController (consulte UIImagePickerController quebra a aparência da barra de status). No seu caso, talvez você queira chamar self. edgesForExtendedLayout UIRectEdgeNone ou tentar ressetar algumas propriedades da barra de status no retorno de chamada do delegado. Há alterações no iOS 6 relacionadas ao tratamento de rotações de visualizações. Apenas orientações definidas nos aplicativos Info. plist são suportadas. Mesmo se você estiver retornando outros. Tente selecionar todas as orientações como suportadas em seu projeto. Manipulando Rotações de Visualização No iOS 6, seu aplicativo suporta as orientações de interface definidas no arquivo Info. plist de seus aplicativos. Um controlador de visualização pode substituir o método supportedInterfaceOrientations para limitar a lista de orientações suportadas. Geralmente, o sistema chama esse método apenas no controlador de visualização raiz da janela ou em um controlador de visualização apresentado para preencher os controladores de exibição filha de tela inteira, utilizando a parte da janela fornecida por seu controlador de visualização pai e não mais participando diretamente de decisões sobre quais rotações são suportadas. A interseção da máscara de orientação de aplicativos e da máscara de orientação dos controladores de exibição é usada para determinar em quais orientações um controlador de visão pode ser rotacionado. Você pode substituir a preferredInterfaceOrientationForPresentation para um controlador de exibição que deve ser apresentado em tela cheia em uma orientação específica. No iOS 5 e versões anteriores, a classe UIViewController exibe exibições apenas no modo retrato. Para suportar orientações adicionais, você deve sobrescrever o método shouldAutorotateToInterfaceOrientation: e retornar YES para quaisquer orientações que sua subclasse suportar. Se as propriedades de redimensionamento automático das suas visualizações estiverem configuradas corretamente, isso pode ser tudo o que você precisa fazer. No entanto, a classe UIViewController fornece ganchos adicionais para você implementar comportamentos adicionais, conforme necessário. Geralmente, se o seu controlador de visualização se destina a ser usado como um controlador de visualização filho, ele deve suportar todas as orientações da interface. Quando ocorre uma rotação para um controlador de visualização visível, o método willRotateToInterfaceOrientation: duration. willAnimateRotationToInterfaceOrientation: duração. e didRotateFromInterfaceOrientation: métodos são chamados durante a rotação. O método viewWillLayoutSubviews também é chamado depois que a visualização é redimensionada e posicionada por seu pai. Se um controlador de visualização não estiver visível quando ocorrer uma alteração de orientação, os métodos de rotação nunca serão chamados. No entanto, o método viewWillLayoutSubviews é chamado quando a exibição se torna visível. Sua implementação desse método pode chamar o método statusBarOrientation para determinar a orientação do dispositivo. Eu tentei isso, mas o aplicativo trava e diz "Orientações suportadas não tem orientação comum". Se eu ativar todas as orientações possíveis, isso funcionará, mas somente se eu girar o telefone para paisagem, ele não fará isso automaticamente quando a visualização for carregada e todas as minhas outras vistas não serão mais alteradas para paisagem, mesmo que eu retorne UIInterfaceOrientationMaskPortrait. Qualquer outra sugestão Muito obrigado Eu realmente aprecio isso ndash Alex G Oct 8 12 at 1:43 Siga os passos abaixo Criar subclasse de UINavigationController substituindo os métodos de rotação. Em AppDelegate, crie uma propriedade do Islandscape BOOL. Quando uma visualização é enviada / exibida / apresentada / rejeitada, ajuste esse valor BOOL. Sample Project Eu criei um projeto de exemplo para isso que está funcionando perfeitamente. Baixe e integre em seu projeto: dropbox / s / nl1wicbx52veq41 / RotationDmeo. zipdl0 Eu tenho procurado a solução por horas Então depois de implementar os métodos necessários em todos os lugares. shouldAutorotate não precisa ser configurado como YES porque já está definido como padrão: Quando é hora de mostrar o UIViewController que precisa da orientação diferente das outras visões, eu criei um UIStoryboardSegue com essa implementação dentro: Dentro do UIStoryboard eu conectei as views com esta segue (mostrando): É apenas importante, você está usando de outra forma a orientação não será determinada novamente. Eu tinha tentado coisas como OR este dentro do UIViewController onde a orientação deveria mudar, e eu também tentei chamar isto dentro de meu UIStoryboardSegues personalizado antes de apresentarViewController e dismissViewController: Mas nenhum deles trabalhou. É claro que o último exemplo não deve ser uma opção, porque se a Apple mudar alguma coisa da sua API isso pode causar problemas dentro do seu aplicativo. Eu também tentei usar o método AppDelegate e sempre determinar a orientação dentro desse método depois de olhar para o UIInterfaceOrientation correto do visibleViewController real, mas, em seguida, às vezes, ele falhava ao alternar de uma para outra orientação. Então, eu ainda estou me perguntando por que isso é tão complicado e parece também não haver qualquer documentação onde é explicado corretamente. Mesmo seguindo essa parte, não me ajudou. answer Mar 31 15 at 11: 47Capítulo 7. Trabalhando com Controladores de Visualização Os controladores View simplificam o gerenciamento de visualizações para muitos aplicativos iOS. Cada controlador de visualização possui uma hierarquia de visualizações, que apresenta um elemento completo de uma interface unificada. Os controladores de visualização permitem que você crie aplicativos que centralizam muitas tarefas, incluindo alterações de orientação e respostas a ações do usuário. Este capítulo analisa o uso de classes baseadas em controller viewash e como aplicá-las a situações do mundo real para ambos os cenários de design de iPhone / iPod e iPad. Como o nome sugere, os controladores de visualização fornecem o componente controlador do padrão de design iOSrsquos ModelndashViewndashController. Cada controlador de visualização gerencia um conjunto de visualizações que compreende um único componente de interface com o usuário em um aplicativo. Os controladores de visualização coordenam o carregamento e a aparência da visualização, além de participar na resposta às interações do usuário. Os controladores de visualização também se harmonizam com o dispositivo e o sistema operacional subjacente. Quando um usuário gira o dispositivo, por exemplo, o controlador de exibição pode atualizar seu layout de viewsrsquo. Quando o SO encontra um cenário de pouca memória, os controladores respondem aos avisos de memória. Em suma, os controladores de visualização fornecem gerenciamento central. Eles negociam com uma gama de requisitos de desenvolvimento ortogonal provenientes de visualizações, modelos, iOS e do próprio dispositivo. Controladores de visão também centralizam metáforas de apresentação. A capacidade de colocar em camadas controladores de visualização em contêineres estende o paradigma a projetos personalizados empolgantes. Os estilos mais comuns de controladores de exibição pai / filho fornecidos pelo sistema incluem controladores de navegação que permitem aos usuários mover a atenção da visualização para exibição, controladores de exibição de página que apresentam livros virtuais, controladores de tabulação que oferecem acesso a vários controladores filho e visualização dividida controladores que oferecem apresentações de lista mestre / detalhes. Veja as visualizações dos controllers São classes sem representação visual, exceto pelas visualizações que gerenciam. Os controladores de visualização ajudam suas visualizações a viver em um ambiente de design de aplicativo maior. O iOS SDK oferece muitas classes de controlador de visualização. Essas classes variam de geral a específica. Herersquos um guia rápido para um subconjunto dos controladores de exibição que você encontra ao construir suas interfaces de aplicativos iOS. O UIViewController é a classe pai dos controladores de visualização e aquele que você usa para gerenciar suas visualizações principais. É o cavalo de batalha dos controladores de visão. Você pode gastar uma grande parte do seu tempo personalizando subclasses dessa classe. A classe UIViewController básica gerencia cada vida útil primária do início ao fim e leva em consideração as mudanças que a visão deve reagir ao longo do caminho. As instâncias do UIViewController são responsáveis ​​por configurar a aparência de uma visualização e as subvisualizações exibidas. Muitas vezes, eles dependem do carregamento dessas informações de arquivos XIB ou de storyboard. Os métodos de instância permitem criar manualmente o layout de exibição no código (loadView) ou adicionar o comportamento após o término do carregamento de uma exibição (viewDidLoad). Reagir a exibições exibidas ou descartadas é outro trabalho que os controladores de visualização manipulam. Estas são as realidades de pertencer a uma aplicação maior. Métodos como viewWillAppear: e viewWillDisappear: permitem que você conclua toda a contabilidade associada ao seu gerenciamento de visualizações. Você pode pré-carregar os dados antes de apresentar ou limpar uma vez que a exibição não será mais mostrada na tela. Cada uma das tarefas mencionadas aqui especifica como uma visualização se ajusta a um aplicativo de envelopamento. O UIViewController faz a intermediação entre as visões e essas demandas externas, permitindo que a visão se modifique para atender a essas necessidades. Como o nome sugere, os controladores de navegação permitem que você faça drill up por meio de hierarquias de visualização baseadas em árvore, que é uma importante estratégia de design de interface primária em membros menores da família de dispositivos iOS e de suporte em tablets. Os controladores de navegação criam as barras de navegação translúcidas que aparecem no topo de muitos aplicativos iOS padrão. Os controladores de navegação permitem que você insira novas visualizações em uma pilha armazenada e gere automaticamente os botões Voltar que mostram o título do controlador de exibição de chamadas. Todos os controladores de navegação usam um controlador de visualização ldquorootrdquo para estabelecer a parte superior da árvore de navegação, permitindo que os botões Voltar o levem de volta à visualização principal. Em tablets, você pode usar uma interface baseada em controlador de navegação para trabalhar com itens de menu baseados em botões e barra, para apresentar apresentações popover ou para integrar com instâncias UISplitViewController para uma experiência de apresentação mestre / detalhada. Transferir a responsabilidade para um controlador de navegação permite concentrar o trabalho de design na criação de telas individuais do controlador de exibição. Você não precisa se preocupar com detalhes de navegação específicos além de informar ao controlador de navegação qual visualização deve ser movida para a próxima. A pilha de histórico e os botões de navegação são manipulados por você. Controladores de barra de guias A classe UITabBarController permite controlar apresentações paralelas em seu aplicativo. Estas são como estações em um rádio. Uma barra de guias ajuda os usuários a selecionar em qual visualização o controlador deve se alinhar, sem que haja uma hierarquia de navegação específica. Cada mundo paralelo opera de forma independente e cada um pode ter sua própria hierarquia de navegação. Você constrói o controlador de visualização ou o controlador de navegação que habita cada guia, e o Cocoa Touch manipula os detalhes de múltiplas visualizações. Por exemplo, quando as instâncias da barra de guias oferecem mais do que um certo número de opções de controle de exibição por vez (cinco na família de dispositivos iPhone, mais em tablets), os usuários podem personalizá-las na tela Mais gt Editar. A tela "Mais gt Edit" permite que os usuários arrastem seus controles favoritos até a barra de botões na parte inferior da tela. Nenhuma programação extra está envolvida. Você ganha guias editáveis ​​gratuitamente. Tudo o que você precisa fazer é solicitá-los por meio da propriedade customizableViewControllers. Controladores de visualização dividida Destinados ao uso em aplicativos tablet, a classe UISplitViewController oferece uma maneira de encapsular um conjunto persistente de dados (geralmente uma tabela) e associar esses dados a uma apresentação detalhada. Você pode ver vistas divididas em ação no aplicativo de e-mail do iPadrsquos. Quando usado na orientação paisagem, uma lista de mensagens aparece no conteúdo da mensagem individual à esquerda aparece à direita. A vista detalhada (o conteúdo da mensagem no Mail) à direita está subordinada à vista principal (lista de mensagens Mailrsquos) à esquerda. Tocar em uma mensagem atualiza a vista do lado direito com seu conteúdo. Na orientação retrato, a vista principal é normalmente oculta. Ele é acessado através de um popover, que é atingido tocando no botão esquerdo da barra superior do viewrsquos split ou através de um gesto de furto (no iOS 5.1 e posterior). Controladores de visualização de página Como os controladores de navegação, os controladores de exibição de guias e os controladores de exibição dividida, os controladores de exibição de página são contêineres para outros controladores de exibição. Eles gerenciam páginas de conteúdo usando uma apresentação de curling de página semelhante a um livro ou um estilo de rolagem. Ao usar o estilo de curling de página, defina o bookrsquos ldquospine, rdquo normalmente ao longo da parte esquerda ou superior da exibição. Crie seu ldquobookrdquo adicionando controladores de visualização de conteúdo individuais. Cada ldquopagerdquo faz a transição para a próxima página usando cachos ou panelas. Específicos para tablets, os controladores popover criam exibições transitórias que aparecem em outros conteúdos de interface existentes. Esses controladores apresentam informações sem assumir toda a tela, da maneira que as exibições modais normalmente fazem. Os popovers geralmente são invocados tocando em um item de botão de barra na interface (embora possam ser criados usando outras técnicas de interação) e são descartados interagindo com o conteúdo que eles apresentam ou tocando fora de sua visualização principal. Popovers são preenchidos com instâncias do controlador de visualização. Crie o controlador de visualização e atribua-o como a propriedade contentViewController popoverrsquos antes de apresentar o popover. Isso permite que os popovers apresentem qualquer faixa de material que você possa projetar em um controlador de exibição padrão, oferecendo excepcional flexibilidade de programação. A partir do iOS 5, você pode criar uma subclasse UINavigationBar e incorporar apresentações personalizadas em suas interfaces de navegação. Use o método de inicialização initWithNavigationBarClass: toolbarClass:. Desenvolvendo com controladores de navegação e exibições divididas A classe UINavigationController oferece uma das maneiras mais importantes de gerenciar interfaces em um dispositivo com espaço de tela limitado. Ele cria uma maneira de os usuários navegarem para cima e para baixo em uma hierarquia de apresentações de interface para criar um GUI virtual muito maior do que o dispositivo. Os controladores de navegação dobram suas GUIs em um esquema puro baseado em árvore. Os usuários viajam por esse esquema usando botões e escolhas que os transportam pela árvore. Você vê controladores de navegação no aplicativo Contatos e em Configurações, onde as seleções levam a novas telas e os botões Voltar movem-se para os anteriores. Vários elementos GUI padrão revelam o uso de controladores de navegação em aplicativos, conforme mostrado na Figura 7-1 (à esquerda). Isso inclui as barras de navegação grandes que aparecem no alto de cada tela, o botão apontando para trás no canto superior esquerdo que aparece quando o usuário faz drill nas hierarquias e os botões de opção no canto superior direito que oferecem outras funcionalidades do aplicativo, como edição. Muitos aplicativos de controlador de navegação são construídos em torno de listas de rolagem, onde os elementos em uma lista levam a novas telas, indicadas pelo indicador de divulgação (chevron cinza) e pelo botão de divulgação de detalhes (i circundado) localizado à direita de cada célula da tabela. Figura 7-1 O controlador de navegação do iPhonersquos (à esquerda) usa divisas cinzas para indicar que as vistas de detalhes serão empurradas na tela quando seus pais forem selecionados. No iPad (à direita), os controladores de visualização dividida usam a tela inteira, separando os elementos de navegação das apresentações detalhadas. O iPad, com seu tamanho de tela grande, não requer o tipo de atalhos que economizam espaço que os controladores de navegação utilizam nos dispositivos da família iPhone. Os aplicativos Tablet podem usar controladores de navegação diretamente, mas o UISplitViewController mostrado na Figura 7-1 (direita) oferece uma apresentação que é mais adequada para o dispositivo mais expansivo. Observe as diferenças entre a implementação do iPhone à esquerda e a implementação do iPad à direita da Figura 7-1. O controlador de visão dividida do iPadrsquos não contém divisas. Quando os itens são tocados, seus dados aparecem na mesma tela, usando a grande área de detalhes do lado direito. O iPhone, sem esse espaço, apresenta divisas que indicam que novas visualizações serão exibidas na tela. Cada abordagem considera o design específico do dispositivo em sua apresentação. As exibições da Caixa de entrada da família do iPhone e do iPad usam elementos de controlador de navegação semelhantes. Estes incluem o botão Voltar (lt iCloud), um botão de opções (Editar) e a descrição na barra de título (a pasta atual, Core iOS). Cada elemento é criado usando a API do controlador de navegação para apresentar uma hierarquia de contas de email e caixas de correio. A diferença está na parte inferior da árvore de navegação, no nível de mensagens individuais que formam as folhas da estrutura de dados. O padrão da família iPhone usa divisas para indicar folhas. Quando selecionado, esses controladores de exibição de folha são colocados na pilha de navegação. Eles se juntam aos outros controladores viewrsquos que rastreiam o progresso de um usuário através da interface. O iPad não empurra suas folhas. Ele os apresenta em uma visão separada e omite os chevrons que, de outra forma, indicam que os usuários atingiram a extensão da travessia da hierarquia. Os controladores de navegação no estilo iPhone também desempenham papéis no iPad. Quando os aplicativos do iPad usam controladores de navegação padrão (estilo iPhone), eles geralmente o fazem em contextos estreitos, como apresentações popover transientes, em que o controlador é apresentado na tela em uma exibição pequena com uma vida útil limitada. Caso contrário, os aplicativos do iPad são incentivados a usar a abordagem de visualização dividida que ocupa a tela inteira. Usando controladores e pilhas de navegação Cada controlador de navegação possui um controlador de visualização raiz. Este controlador forma a base de sua pilha. Você pode empurrar programaticamente outros controladores para a pilha enquanto o usuário faz escolhas enquanto navega pela árvore do modelrsquos. Embora a árvore em si possa ser multidimensional, o caminho userrsquos (essencialmente sua história) é sempre uma linha reta representando as escolhas já feitas até o momento. A mudança para uma nova opção amplia a trilha de navegação e cria automaticamente um botão Voltar sempre que um novo controlador de visualização é colocado na pilha. Os usuários podem tocar em um botão Voltar para tirar os controladores da pilha. O nome de cada botão é o título do controlador de visualização mais recente. À medida que você retorna através da pilha de controladores de visualização anteriores, cada botão Voltar indica o controlador de visualização que pode ser retornado. Os usuários podem voltar até alcançar a raiz. Então eles não podem ir mais longe. A raiz é a base da árvore e você não pode ultrapassar essa raiz. Esse design baseado em pilha permanece mesmo quando você planeja usar apenas um controlador de visualização. Você pode querer aproveitar a barra de navegação interna do UINavigationControllerrsquos para criar um utilitário simples que usa um menu de dois botões, por exemplo. Isso desconsideraria qualquer vantagem de navegação da pilha. Você ainda precisa definir esse controlador como root via initWithRootViewController :. Empurrando e Popping View Controllers Adicione novos itens à pilha de navegação, empurrando um novo controlador com pushViewController: animated. Cada controlador de exibição fornece uma propriedade navigationController. Essa propriedade aponta para o controlador de navegação do qual este controlador está participando. A propriedade isnil se o controlador não for colocado em uma pilha de navegação. Use a propriedade navigationController para enviar um novo controlador de visualização para a pilha de navegação e chamar o método push no controlador de navegação. Quando pressionado, o novo controlador desliza na tela a partir da direita (supondo que você defina animado para YES). Um botão Voltar apontando para a esquerda aparece, levando você um passo para trás na pilha. O botão Voltar usa uma divisa junto com o título do controlador de visualização anterior na pilha de navegação. Substitua a divisa por uma imagem personalizada, definindo a propriedade backIndicatorImage. Tenha sempre cuidado ao substituir os elementos padrão da Apple. Certifique-se de manter o espírito do Apple Human Interface Guidelines (HIG). Você pode empurrar uma nova visão por vários motivos. Normalmente, elas envolvem a navegação para exibições de especialidade, como exibições de detalhes ou o detalhamento de uma estrutura de arquivos ou hierarquia de preferências. Você pode empurrar os controladores para a pilha do controlador de navegação depois que o usuário tocar em um botão, item de tabela ou acessório de divulgação. Therersquos pouca razão para alguma vez subclassificar o UINavigationController. Realize solicitações de push e personalização da barra de navegação (como configurar um título ou botões barrsquos) dentro das subclasses UIViewController. A personalização é passada para o controlador de navegação dos controladores filhos. Na maioria das vezes, você não acessa o controlador de navegação diretamente. As exceções a essa regra incluem gerenciar os botões barrotsquos de navegação, alterar a aparência barrsquos e inicializar com uma classe de barra de navegação personalizada. Você pode alterar um estilo de barra ou sua cor de matiz acessando a propriedade thenavigationBar diretamente, da seguinte forma: Lembre-se de que no iOS 7, a Apple adicionou barTintColor para colorir o fundo da barra em vez de tintColor. A propriedade tintColor é reaproveitada para colorir os itens do botão da barra. Para adicionar novos botões, você modifica seu navigationItem, que fornece uma classe representacional que descreve o conteúdo mostrado na barra de navegação, incluindo seus itens de botão da barra esquerda e direita e sua visualização de título. Herersquos como você pode atribuir um botão à barra: self. navigationItem. rightBarButtonItem UIBarButtonItem alloc initWithTitle: Estilo de ação: UIBarButtonItemStylePlain target: self action: selector (performAção :) Para remover um botão, atribua o item a zero. Itens de botão de barra não são vistas. São classes que contêm títulos, estilos e informações de retorno de chamada que os itens de navegação e as barras de ferramentas usam para criar botões reais em interfaces. O iOS não fornece acesso a visualizações de botão criadas por itens de botão de barra e seus itens de navegação. A partir do iOS 5, você pode adicionar vários itens de botão de barra à esquerda e à direita. Atribua um array às propriedades rightBarButtonItems (observe o s) ou leftBarButtonItems para o item de navegação: O foco de design do iOS 7 é o seu conteúdo de aplicativo mais especificamente, seu conteúdo de usuário. Bordas e sombras foram removidas e transparência foi adicionada nas barras de navegação e outros elementos da interface do usuário. Essa alteração afeta significativamente o layout de suas visualizações, especialmente quando você usa uma barra de navegação. A partir do iOS 7, todos os controladores de visualização usam o layout de tela cheia. A propriedade wantsFullScreenLayout no UIViewController foi descontinuada e configurá-la como NO provavelmente levará a um layout muito inesperado. Com o layout de tela inteira, o controlador de visualização dimensionará sua visualização para preencher a tela inteira, passando totalmente sob a barra de status do sistema translúcido. Além disso, por padrão, todas as barras no iOS 7 agora são translúcidas para revelar ainda mais o conteúdo subjacente. O fluxo de conteúdo sob barras mudará seu conteúdo de formas estranhas nas versões anteriores. Você deve incluir ativamente a área abaixo da barra de status e suas próprias barras no layout. Para fornecer mais controle sobre o posicionamento, o UIViewController agora fornece várias novas propriedades de layout. Gerencie a visibilidade da barra de status no nível do controlador de visualização implementando o prefersStatusBarHidden em suas subclasses e retornando um Booleano apropriado. Muitas das novas propriedades permitem o posicionamento ou o dimensionamento de visualizações com base nas barras atualmente exibidas. Para os controladores de visualização, agora você pode especificar quais arestas da vista devem ser estendidas em barras translúcidas definindo edgesForExtendedLayout. Por padrão, essa propriedade é UIRectEdgeAllmdash, o que significa que todas as suas arestas se estenderão pelos elementos translúcidos, conforme mostrado na Figura 7-2 (esquerda). Você também pode especificar qualquer aresta (s) específica (s) ou UIRectEdgeNone, parando a aresta da vista de conteúdo quando esta atingir a barra, como mostra a Figura 7-2 (direita). Por padrão, edgesForExtendedLayout também inclui barras opacas. SetextendedLayoutIncludesOpaqueBars para NO para alterar esse comportamento. Figura 7-2 No iOS 7, edgesForExtendedLayout no UIViewController controla a borda da visualização usada para o layout. UIRectEdgeAll, o padrão, estende a borda pelas barras translúcidas (à esquerda). UIRectEdgeNone pára a aresta na extensão das barras (direita). As visualizações de rolagem também são afetadas pela barra de status do sistema e pelas barras implementadas pelo desenvolvedor (barra de navegação, barra de ferramentas e barra de guias). Por padrão, o UIScrollViews ajusta automaticamente seus insets de conteúdo para manipular esses elementos da barra. Para desativar esse comportamento e gerenciar manualmente os inserts de exibição de rolagem, defina automaticAdjustsScrollViewInsets como NO. Por fim, para ajudar na criação de conteúdo em seus modos de exibição, o iOS 7 fornece as propriedades topLayoutGuide e bottomLayoutGuide. Essas propriedades indicam as arestas da barra superior e inferior na exibição view do controllerrsquos. A localização representada depende das barras visíveis. Barra de status, mas nenhuma barra de navegação visiblemdashbottom da barra de status. Barra de navegação visiblemdashbottom da barra de navegação. Nenhum status ou barra de navegação visiblemdashtop da tela. Barra de ferramentas ou guia barra visiblemdashtop da barra de ferramentas ou barra de guias. Nenhuma barra de ferramentas ou barra de tabulação visiblemdashbottom da tela. Use essas propriedades para criar restrições relativas, posicionando suas subvisualizações relativas às arestas da barra, independentemente do local do quadro ou do pré-conhecimento da visibilidade da barra. Use-os com restrições de Layout Automático no Interface Builder (IB) ou no código do layout. Fora do Layout automático, use guias no posicionamento baseado em quadros. Faça referência ao valor de deslocamento na propriedade length do guidersquos. Receita: A classe de item de navegação Os objetos que preenchem a barra de navegação são colocados usando a classe UINavigationItem, que é uma classe que armazena informações sobre esses objetos. As propriedades do item de navegação incluem os itens dos botões da barra esquerda e direita, o título mostrado na barra, a exibição usada para mostrar o título e qualquer botão Voltar usado para navegar de volta a partir da exibição atual. Essa classe permite que você anexe botões, texto e outros objetos da interface do usuário em três locais principais: a esquerda, o centro e a direita da barra de navegação. Normalmente, isso funciona como um botão normal à direita, algum texto (geralmente o título UIViewControllerrsquos) no meio e um botão Back-style à esquerda. Mas você não está limitado a este layout. Você pode adicionar controles personalizados a qualquer um dos três locais: à esquerda, ao centro (área de título) e à direita. Você pode criar barras de navegação com campos de pesquisa no meio, ou controles de segmento, barras de ferramentas, imagens e muito mais. Além disso, você pode adicionar vários itens aos arrays de botões esquerdo e direito. Itrsquos tudo fácil de modificar. Títulos e botões Voltar A área do título central é especialmente personalizável. Você pode atribuir um título ao item de navegação da seguinte forma: self. navigationItem. title My Title Isso equivale a definir a propriedade de título view controllerrsquos diretamente. A maneira mais simples de personalizar o título real é usar a propriedade de título do controlador de exibição filho, em vez do item de navegação: Quando atribuído, o controlador de navegação usa o título para estabelecer o texto Voltar para o backrdquo. Se você pressionar um novo controlador na parte superior de um controlador chamado Hello, o botão Voltar indica que ele está vinculado a Hello. Você também pode substituir o título com base em texto por um modo de exibição personalizado, como um controle. Esse código adiciona um controle segmentado personalizado, mas pode ser uma visualização de imagem, um passo ou qualquer outra coisa: self. navigationItem. titleView UISegmentedControl alloc initWithItems: items As macros simplificam seu trabalho ao criar botões de barra porque a tarefa de criação é tão repetitiva. A macro a seguir cria um item de botão básico: define BARBUTTON (TITLE, SELECTOR) UIBarButtonItem alloc initWithTitle: estilo TITLE: UIBarButtonItemStylePlain target: self action: SELECTOR Fornece um título e um seletor para chamar. Cada chamada para essa macro especifica apenas o título e o seletor, aumentando a legibilidade dos codersquos: self. navigationItem. rightBarButtonItem BARBUTTON (Push, selector (push :)) Essa versão da macro pressupõe que o destino seja self, o que é bastante comum, embora você possa facilmente adaptar isso. A macro a seguir adiciona um destino que você especifica: define BARBUTTONTARGET (TITLE, TARGET, SELECTOR) UIBarButtonItem alocação initWithTitle: TITLE style: UIBarButtonItemStylePlain target: TARGET action: SELECTOR O vocabulário dos botões de barra utilizados varia de acordo com as demandas de aplicativos específicas. Itrsquos fácil de criar macros para itens do sistema fornecidos pela Apple, itens de imagem criados a partir de recursos de imagem e itens de exibição personalizada, que podem incorporar controles e outros elementos de botão não-barra. Recipe 7-1 combines these features to demonstrate how controller titles and navigation items build together during drilling. It offers a super-simple interface: You select the title for the next item you want to push onto the navigation stack, and then you push it on. This allows you to see how the navigation controller stack grows using default behavior. Recipe 7-1 Basic Navigation Drilling // Array of strings - (NSArray )fooBarArray return FooBarBazQux componentsSeparatedByString: // Push a new controller onto the stack - (void)push:(id)sender NSString newTitle self fooBarArrayseg. selectedSegmentIndex UIViewController newController TestBedViewController alloc init newController. edgesForExtendedLayout UIRectEdgeNone newController. title newTitle self. navigationController pushViewController:newController animated:YES - (void)loadView self. view UIView alloc init self. view. backgroundColor UIColor whiteColor // Establish a button to push new controllers self. navigationItem. rightBarButtonItem BARBUTTON(Push, selector(push:)) // Create a segmented control to pick the next title seg UISegmentedControl alloc initWithItems: self fooBarArray seg. selectedSegmentIndex 0 self. view addSubview:seg PREPCONSTRAINTS(seg) UILabel label self labelWithTitle:Select Title for Pushed Controller self. view addSubview:label PREPCONSTRAINTS(label) id t opLayoutGuide self. topLayoutGuide CONSTRAIN(self. view, label, H:-label(gt0)-) CONSTRAIN(self. view, seg, H:-seg(gt0)-) CONSTRAINVIEWS(self. view, V:topLayoutGuide-label-seg, NSDictionaryOfVariableBindings(seg, label, topLayoutGuide)) Get This Recipersquos Code To find this recipersquos full sample project, point your browser to github/erica/iOS-7-Cookbook and go to the folder for Chapter 7. Recipe: Modal Presentation With normal navigation controllers, you push your way along views, stopping occasionally to pop back to previous views. That approach assumes that yoursquore drilling your way up and down a set of data that matches the tree-based view structure yoursquore using. Modal presentation offers another way to show a view controller. After you send the presentViewController:animated:completion: message to a view controller, the specified view controller appears on the screen and takes control until itrsquos dismissed with dismissViewControllerAnimated:completion. This enables you to add special-purpose dialogs to your applications that go beyond alert views. Typically, modal controllers prompt users to pick data such as contacts from Contacts or photos from Photos or perform a short-lived task such as sending e-mail or setting preferences. Use modal controllers in any setting where it makes sense to perform a limited-time task that lies outside the normal scope of the active view controller. Modal presentations can use four transition styles: Slide mdashThis transition style slides a new view over the old. Flip mdashThis transition style turns a view over to the ldquobackrdquo of the presentation. Fademdash This transition style dissolves the new view into visibility. Curl mdashThis transition style makes the primary view curl up out of the way to reveal the new view beneath it. Set these styles in the modalTransitionStyle property of the presented view controller. The standard, UIModalTransitionStyleCoverVertical, slides the modal view up and over the current view controller. When dismissed, it slides back down. UIModalTransitionStyleFlipHorizontal performs a back-to-front flip from right to left. It looks as if yoursquore revealing the back side of the currently presented view. When dismissed, it flips back, left to right. UIModalTransitionStyleCrossDissolve fades the new view in over the previous one. On dismissal, it fades back to the original view. Use UIModalTransitionStylePartialCurl to curl up content (in the way the Maps application does) to reveal a modal settings view ldquounderneathrdquo the primary view controller. On the iPhone and iPod touch, modal controllers always fully take over the screen. The iPad offers more nuanced presentations. The iPad offers five presentation styles set by the modalPresentationStyle property: Full screen mdashA full-screen presentation (UIModalPresentationFullScreen) is the default on the iPhone, where the new modal view completely covers both the screen and any existing content. This is the only presentation style that is legal for curls any other presentation style raises a runtime exception, crashing the application. Page sheet mdashIn the page sheet style (UIModalPresentationPageSheet), coverage defaults to a portrait aspect ratio, so the modal view controller completely covers the screen in portrait mode and partially covers the screen in landscape mode, as if a portrait-aligned piece of paper were added to the display. Form sheet mdashThe form sheet style (UIModalPresentationFormSheet) display covers a small center portion of the screen, allowing you to shift focus to the modal element while retaining the maximum visibility of the primary application view. Current context mdashThis is the presentation style of the viewrsquos parent view controller (UIModalPresentationCurrentContext). Custom mdashThis custom presentation style (UIModalPresentationCustom), managed by the Custom Transitions API, was added in iOS 7. These styles are best experienced in landscape mode to visually differentiate the page-sheet presentation from the full-screen one. iOS 7 introduces a model for creating custom transitions between view controllers to augment those provided by the system. Custom transitions provide nearly unlimited flexibility in creating creative transitions that can be used nearly anywhere that view controllers currently transition, including modal presentation and navigation controller stack changes. Presenting a Custom Modal Information View Presenting a modal controller branches off from your primary navigation path, introducing a new interface that takes charge until your user explicitly dismisses it. You present a modal controller like this: self presentViewController:someControllerInstance animated:YES completion:nil The controller that is presented can be any kind of view controller subclass, as well. In the case of a navigation controller, the modal presentation can have its own navigation hierarchy built as a chain of interactions. Use the completion block to finish up any tasks you need to perform after the view controller has animated into place. Always provide a Done option of some kind to allow users to dismiss the controller. The easiest way to accomplish this is to present a navigation controller and add a bar button to its navigation items with an action: - (IBAction)done:(id)sender self dismissViewControllerAnimated:YES completion:nil Storyboards simplify the creation of modal controller elements. Drag in a navigation controller instance, along with its paired view controller, and add a Done button to the provided navigation bar. Set the view controllerrsquos class to your custom modal type and connect the Done button to thedone: method. Name your navigation controller in the Attributes inspector so that you can use that identifier to load it. You can either add the modal components to your primary storyboard or create them in a separate file. Recipe 7-2 loads a custom file (Modal DeviceType. storyboard), but you can just as easily add the elements in your MainStoryboard DeviceType file. Recipe 7-2 provides the key pieces for creating modal elements. The presentation is performed in the applicationrsquos main view controller hierarchy. Here, users select the transition and presentation styles from segmented controls, but these are normally chosen in advance by the developer and set in code or in IB. This recipe offers a toolbox that you can test on each platform, using each orientation to explore how each option looks. As of the initial iOS 7 release, a well-reported issue exists in the full-screen flip transition presented in Recipe 7-2 . The navigation bar contents drop abruptly into position at the end of the animation. Hopefully, this issue will be resolved in a future iOS release. Recipe 7-2 Presenting and Dismissing a Modal Controller // Presenting the controller - (void)action:(id)sender // Load info controller from storyboard UIStoryboard storyBoard UIStoryboard storyboardWithName: (ISIPAD. Modal iPhone) bundle:NSBundle mainBundle UINavigationController navController storyBoard instantiateViewControllerWithIdentifier: infoNavigationController // Select the transition style int styleSegment segmentedControl selectedSegmentIndex int transitionStyles4 UIModalTransitionStyleCoverVertical, UIModalTransitionStyleCrossDissolve, UIModalTransitionStyleFlipHorizontal, UIModalTransitionStylePartialCurl navController. modalTransitionStyle transitionStylesstyleSegment // Select the presentation style for iPad only if (ISIPAD) int presentationSegment iPadStyleControl selectedSegmentIndex int presentationStyles3 UIModalPresentationFullScreen, UIModalPresentationPageSheet, UIModalPresentationFormSheet if (navController. modalTransitionStyle UIModalTransitionStylePartia lCurl) // Partial curl with any non-full-screen presentation // raises an exception navController. modalPresentationStyle UIModalPresentationFullScreen iPadStyleControl setSelectedSegmentIndex:0 else navController. modalPresentationStyle presentationStylespresentationSegment self. navigationController presentViewController:navController animated:YES completion:nil - (void)loadView self. view UIView alloc init self. view. backgroundColor UIColor whiteColor self. navigationItem. rightBarButtonItem BARBUTTON(Action, selector(action:)) segmentedControl UISegmentedControl alloc initWithItems: Slide Fade Flip Curl componentsSeparatedByString: segmentedControl setSelectedSegmentIndex:0 self. navigationItem. titleView segmentedControl if (ISIPAD) NSArray presentationChoices NSArray arrayWithObjects:Full Screen, Page Sheet, Form Sheet, nil iPadStyleControl UISegmentedControl alloc initWithItems: presentationChoices iPadStyleControl setSelectedSegmentIndex:0 self. view addSubview:iPadStyleCont rol PREPCONSTRAINTS(iPadStyleControl) CENTERVIEWH(self. view, iPadStyleControl) id topLayoutGuide self. topLayoutGuide CONSTRAINVIEWS(self. view, V:topLayoutGuide-iPadStyleControl, NSDictionaryOfVariableBindings(topLayoutGuide, iPadStyleControl)) Get This Recipersquos Code To find this recipersquos full sample project, point your browser to github/erica/iOS-7-Cookbook and go to the folder for Chapter 7. Recipe: Building Split View Controllers Using split view controllers is the preferred way to present hierarchically driven navigation on the iPad. Such a controller generally consists of a table of contents on the left and a detail view on the right, although the class (and Applersquos guidelines) is not limited to this presentation style. The heart of the class consists of the notion of an organizing section (master) and a presentation section (detail), both of which can appear onscreen simultaneously in landscape orientation, and whose organizing section optionally converts to a popover in portrait orientation. (You can override this default behavior by implementing splitViewController:shouldHideViewController:inOrientation: in your delegate, letting your split view show both sections in portrait mode.) Figure 7-3 shows the very basic split view controller built by Recipe 7-3 in landscape (left) and portrait (right) orientations. This controller sets the color of the detail view by selecting an item from the list in the root view. In landscape, both views are shown at once. In portrait orientation, the user must tap the upper-left button on the detail view to access the root view as a popover or use an optional swipe gesture. When programming for this orientation, be aware that the popover can interfere with detail view, as it is presented over that view design accordingly. Figure 7-3 At their simplest, split view controllers consist of an organizing pane and a detail view pane. The organizing pane, which you see in this figure, is normally hidden in portrait orientation (right). Users view it via a popover accessed from a navigation bar button or invoke it with a swipe gesture. The code in Recipe 7-3 builds three separate objects: the master and detail view controllers and the split view controller that owns the first two. The split view controller always contains two children, the master at index 0 and the detail at index 1. Yoursquoll want to add the master and detail controllers to navigation controller shells, to provide a consistent interface. In the case of the detail controller, this provides a home for the bar button in portrait orientation. The following method builds the two child view controllers, embeds them into navigation controllers, adds them to a view controller array, and returns a new split view controller that hosts those views: - (UISplitViewController )splitViewController // Create the navigation-embedded root (master) view ColorTableViewController rootVC ColorTableViewController alloc init rootVC. title Colors // make sure to set the title UINavigationController rootNav UINavigationController alloc initWithRootViewController:rootVC // Create the navigation-run detail view DetailViewController detailVC DetailViewController controller UINavigationController detailNav UINavigationController alloc initWithRootViewController:detailVC // Add both to the split view controller UISplitViewController svc UISplitViewController alloc init svc. viewControllers rootNav, detailNav svc. delegate detailVC The master view controller is often some kind of table view controller, as is the one in Recipe 7-3 . What you see here is pretty much as bare bones as tables get. It is a list of color items (specifically, UIColor method names), each one with a cell title that is tinted to match that color. When an item is selected, the controller uses its built-in splitViewController property to send a request to its detail view. This property returns the split view controller that owns the root view. From there, the controller can retrieve the split viewrsquos delegate, which has been assigned to the detail view. By casting that delegate to the detail view controllerrsquos class, the root view can affect the detail view more meaningfully. In this extremely simple example, the selected cellrsquos text tint is applied to the detail viewrsquos background color. Make sure you set the root view controllerrsquos title property. It is used to set the text for the button that appears in the detail view in portrait mode. Recipe 7-3 rsquos DetailViewController class is about as skeletal an implementation as you can get. It provides the most basic functionality you need to provide a detail view implementation with split view controllers. This consists of the will-hide/will-show method pair that adds and hides that all-important bar button for the detail view. When the split view controller converts the master view controller into a popover controller in portrait orientation, it passes that new controller to the detail view controller. It is the detail controllerrsquos job to retain and handle that popover until the interface returns to landscape orientation. In this skeletal class definition, a strong property holds onto the popover for the duration of portrait interaction. Recipe 7-3 Building Detail and Master Views for a Split View Controller interface DetailViewController. UIViewController ltUIPopoverControllerDelegate, UISplitViewControllerDelegategt property (nonatomic, strong) UIPopoverController popoverController end implementation DetailViewController (instancetype)controller DetailViewController controller DetailViewController alloc init controller. view. backgroundColor UIColor blackColor return controller // Called upon going into portrait mode, hiding the normal table view - (void)splitViewController:(UISplitViewController)svc willHideViewController:(UIViewController )aViewController withBarButtonItem:(UIBarButtonItem)barButtonItem forPopoverController:(UIPopoverController)aPopoverController barButtonItem. title aViewController. title self. navigationItem. leftBarButtonItem barButtonItem self. popoverController aPopoverController // Called upon going into landscape mode - (void)splitViewController:(UISplitViewController)svc willShowViewController:(UIViewController )aViewController invalidatingBarButtonItem:(UIBarButtonItem )barButtonItem self. n avigationItem. leftBarButtonItem nil self. popoverController nil // Use this to avoid the popover hiding in portrait. // When omitted, you get the default behavior. / - (BOOL)splitViewController:(UISplitViewController )svc shouldHideViewController:(UIViewController )vc inOrientation:(UIInterfaceOrientation)orientation return NO / end interface ColorTableViewController. UITableViewController end implementation ColorTableViewController (instancetype)controller ColorTableViewController controller ColorTableViewController alloc init controller. title Colors return controller - (NSInteger)numberOfSectionsInTableView:(UITableView )tableView return 1 - (NSArray )selectors return blackColor, redColor, greenColor, blueColor, cyanColor, yellowColor, magentaColor, orangeColor, purpleColor, brownColor - (NSInteger)tableView:(UITableView )tableView numberOfRowsInSection:(NSInteger)section return self selectors. count - (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath )indexPath UITableViewCell cell tableView dequeueReusableCellWithIdentifier:generic if (cell) cell UITableViewCell alloc initWithStyle: UITableViewCellStyleDefault reuseIdentifier:generic // Set title and color NSString item self selectorsindexPath. row cell. textLabel. text item cell. textLabel. textColor UIColor performSelector:NSSelectorFromString(item) w ithObject:nil - (void)tableView:(UITableView )tableView didSelectRowAtIndexPath:(NSIndexPath )indexPath // On selection, update the main view background color UINavigationController nav self. splitViewController. viewControllers lastObject UIViewController controller nav topViewController UITableViewCell cell tableView cellForRowAtIndexPath:indexPath controller. view. backgroundColor cell. textLabel. textColor end Get This Recipersquos Code To find this recipersquos full sample project, point your browser to github/erica/iOS-7-Cookbook and go to the folder for Chapter 7. Recipe: Creating Universal Split View/Navigation Apps Recipe 7-4 modifies Recipe 7-3 rsquos split view controller to provide a functionally equivalent application that runs properly on both iPhone and iPad platforms. Accomplishing this takes several steps that add to Recipe 7-3 rsquos code base. You do not have to remove functionality from the split view controller approach, but you must provide alternatives in several places. Recipe 7-4 uses a macro to determine whether the code is being run on an iPad - or iPhone-style device. It leverages the UIKit user interface idiom as follows: define ISIPAD (UIUSERINTERFACEIDIOM() UIUserInterfaceIdiomPad) This macro returns YES when the device characteristics are iPad-like rather than iPhone-like (such as on the iPhone or iPod touch). First introduced in iOS 3.2, which introduced the iPad as a new hardware platform, idioms allow you to perform runtime checks in your code to provide interface choices that fit with the deployed platform. In an iPhone deployment, the detail view controller code remains identical to Recipe 7-3 . but to be displayed, it must be pushed onto the navigation stack rather than shown side by side in a split view. The navigation controller is set up as the primary view for the application window rather than the split view. A simple check at application launch lets your code choose which approach to use: - (UINavigationController )navWithColorTableViewController ColorTableViewController rootVC ColorTableViewController alloc init rootVC. title Colors UINavigationController nav UINavigationController alloc initWithRootViewController:rootVC return nav - (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions window UIWindow alloc initWithFrame: UIScreen mainScreen bounds UIViewController rootVC nil if (ISIPAD) rootVC self splitViewController else rootVC self navWithColorTableViewController rootVC. edgesForExtendedLayout UIRectEdgeNone window. rootViewController rootVC window makeKeyAndVisible return YES The rest of the story lies in the two methods of Recipe 7-4 . within the color-picking table view controller. Two key checks decide whether to show disclosure accessories and how to respond to table taps: On the iPad, disclosure indicators should never be used at the last level of detail presentation. On the iPhone, they indicate that a new view will be pushed on selection. Checking for deployment platform lets your code choose whether to include these accessories in cells. When yoursquore working with the iPhone, therersquos no option for using split views, so your code must push a new detail view onto the navigation controller stack. Compare this to the iPad code, which only needs to reach out to an existing detail view and update its background color. In real-world deployment, these two checks would likely expand in complexity beyond the details shown in this simple recipe. Yoursquod want to add a check to your model to determine whether you are, indeed, at the lowest level of the tree hierarchy before suppressing disclosure accessories. Similarly, you might need to update or replace presentations in your detail view controller. Recipe 7-4 Adding Universal Support for Split View Alternatives - (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath )indexPath UITableViewCell cell tableView dequeueReusableCellWithIdentifier:generic if (cell) cell UITableViewCell alloc initWithStyle:UITableViewCellStyleDefault reuseIdentifier:generic NSString item self selectorsindexPath. row cell. textLabel. text item cell. textLabel. textColor UIColor performSelector:NSSelectorFromString(item) withObject:nil cell. accessoryType ISIPAD UITableViewCellAccessoryNone : UITableViewCellAccessoryDisclosureIndicator - (void)tableView:(UITableView )tableView didSelectRowAtIndexPath:(NSIndexPath )indexPath UITableViewCell cell tableView cellForRowAtIndexPath:indexPath if (ISIPAD) UINavigationController nav self. splitViewController. viewControllers lastObject UIViewController controller nav topViewController controller. view. backgroundColor cell. textLabel. textColor else DetailViewController controller DetailViewController controller controller. view. backgroundColor cell. textLabel. textColor controller. title cell. textLabel. text self. navigationController pushViewController:controller animated:YES Get This Recipersquos Code To find this recipersquos full sample project, point your browser to github/erica/iOS-7-Cookbook and go to the folder for Chapter 7. On the iPhone and iPod touch, the UITabBarController class allows users to move between multiple view controllers and to customize the bar at the bottom of the screen. This is best seen in the music application. It offers one-tap access to different views and a More button that leads to user selection and editing of the bottom bar. Tab bars are not recommended for use as a primary design pattern on the iPad, although Apple supports their use when needed, especially in split views and popovers. With tab bars, you donrsquot push views the way you do with navigation bars. Instead, you assemble a collection of controllers (they can individually be UIViewControllers, UINavigationControllers, or any other kind of view controllers) and add them to a tab bar by setting the barrsquos viewControllers property. Cocoa Touch does the rest of the work for you. Set allowsCustomizing to YES to enable end-user reordering of the bar. Recipe 7-5 creates 11 simple view controllers of the BrightnessController class. This class sets the background to a specified gray levelmdashin this case, from 0 to 100, in steps of 10. Figure 7-4 (left) shows the interface in its default mode, with the first four items and a More button displayed. Figure 7-4 Tab bar controllers allow users to pick view controllers from a bar at the bottom of the screen (left) and customize the bar from a list of available view controllers (right). Users may reorder tabs by selecting the More option and then tapping Edit. This opens the configuration panel shown in Figure 7-4 (right). These 11 view controllers offer options a user can navigate through and select from. Note that the navigation bar in Figure 7-4 (right) has not been converted to the standard flat UI appearance as of the iOS 7 release. Note that the translucent navigation bar background tint is black throughout the entire interface. Apple provides the UIAppearance protocol, which allows you to customize UI properties for all instances of a given class. Recipe 7-5 uses this functionality to tint its navigation barrsquos background black: UINavigationBar appearance setBarTintColor:UIColor blackColor Starting with iOS 7, tintColor no longer tints the background of bars, such as the navigation bar. To tint the background, use the barTintColor property. This recipe adds its 11 controllers twice. The first time it assigns them to the list of view controllers available to the user: The second time it specifies that the user can select from the entire list when interactively customizing the bottom tab bar: The second line is optional the first is mandatory. After setting up the view controllers, you can add all or some to the customizable list. If you donrsquot, you still can see the extra view controllers by tapping the More button, but users wonrsquot be able to include them in the main tab bar on demand. Tab art appears inverted in color on the More screen. According to Apple, this is the expected and proper behavior. Apple has no plans to change this. It does provide an interesting view contrast when your 100 black swatch appears as pure white on that screen. In addition, in iOS 7, the icon and text for items are now tinted with the inherited tintColor for the application. Recipe 7-5 Creating a Tab Bar View Controller pragma mark - BrightnessController interface BrightnessController. UIViewController end implementation BrightnessController int brightness // Create a swatch for the tab icon using standard Quartz // and UIKit image calls - (UIImage)buildSwatch:(int)aBrightness CGRect rect CGRectMake(0.0f, 0.0f, 30.0f, 30.0f) UIGraphicsBeginImageContext(rect. size) UIBezierPath path UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:4.0f UIColor blackColor colorWithAlphaComponent:(float) aBrightness / 10.0f set path fill UIImage image UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() // The view controller consists of a background color // and a tab bar item icon -(BrightnessController )initWithBrightness:(int)aBrightness self super init brightness aBrightness self. title NSString stringWithFormat:d, brightness 10 self. tabBarItem UITabBarItem alloc initWithTitle:self. title image:self buildSwatch:brightness tag:0 self. view. autoresizesSubviews YES self. view. autoresizingMask UIViewAutoresizingFlexibleWidth UIViewAutoresizingFlexibleH eight return self // Tint the background - (void)loadView self. view UIView alloc init self. view. backgroundColor UIColor colorWithWhite:(brightness / 10.0f) alpha:1.0f (id)controllerWithBrightness:(int)brightness BrightnessController controller BrightnessController alloc initWithBrightness:brightness return controller end pragma mark - Application Setup interface TestBedAppDelegate. NSObject ltUIApplicationDelegate, UITabBarControllerDelegategt property (nonatomic, strong) UIWindow window end implementation TestBedAppDelegate UITabBarController tabBarController - (void)applicationDidFinishLaunching:(UIApplication )application // Globally use a black tint for nav bars UINavigationBar appearance setBarTintColor:UIColor blackColor // Build an array of controllers NSMutableArray controllers NSMutableArray array for (int i 0 i lt 10 i) BrightnessController controller BrightnessController controllerWithBrightness:i UINavigationController nav UINavigationController alloc initWithRootViewController:controller nav. navigationBar. barStyle UIBarStyleBlackTranslucent controllers addObject:nav tabBarController UITabBarController alloc init tabBarController. tabBar. barTintColor UIColor blackColor tabBarController. tabBar. translucent NO tabBarController. viewControllers controllers tabBarController. customizableViewControllers controllers tabBarController. delegate self w indow UIWindow alloc initWithFrame:UIScreen mainScreen bounds window. tintColor COOKBOOKPURPLECOLOR tabBarController. edgesForExtendedLayout UIRectEdgeNone window. rootViewController tabBarController window makeKeyAndVisible return YES end Get This Recipersquos Code To find this recipersquos full sample project, point your browser to github/erica/iOS-7-Cookbook and go to the folder for Chapter 7. Remembering Tab State On iOS, persistence is golden. When starting or resuming your application from termination or interruption, always return users to a state that closely matches where they left off. This lets your users continue whatever tasks they were involved with and provides a user interface that matches the previous session. Listing 7-1 introduces an example of doing exactly that. This update to Recipe 7-5 stores both the current tab order and the currently selected tab, and it does so whenever those items are updated. When a user launches the application, the code searches for previous settings and applies them if they are found. To respond to updates, a tab bar delegate must declare the UITabBarControllerDelegate protocol. The approach used here depends on two delegate methods. The first, tabBarController:didEndCustomizingViewControllers:changed. provides the current array of view controllers after the user has customized them with the More gt Edit screen. This code captures their titles (10, 20, and so on) and uses that information to relate a name to each view controller. The second delegate method is tabBarController:didSelectViewController. The tab bar controller calls this method each time a user selects a new tab. By capturing the selectedIndex, this code stores the controller number relative to the current array. In this example, these values are stored using iOSrsquos built-in user defaults system, NSUserDefaults. This preferences system works very much like a large mutable dictionary. Set values for keys by using setObject:forKey. as shown here: NSUserDefaults standardUserDefaults setObject:titles forKey:tabOrder Then retrieve them with objectForKey. like so: NSArray titles NSUserDefaults standardUserDefaults objectForKey:tabOrder Synchronizing your settings ensures that the stored defaults dictionary matches your changes: NSUserDefaults standardUserDefaults synchronize When the application launches, it checks for previous settings describing the last selected tab order and selected tab. If it finds them, it uses these settings to set up the tabs and select a tab to make active. Because the titles contain the information about what brightness value to show, this code converts the stored title from text to a number and divides that number by 10 to send to the initialization method. Most applications arenrsquot based on such a simple numeric system. If you use titles to store your tab bar order, make sure you name your view controllers meaningfully and in a way that lets you match a view controller with the tab ordering. You could also store an array of the view tags as NSNumbers or, better yet, use the NSKeyedArchiver class. Archiving lets you rebuild views using state information that you store on termination. Another option is the state preservation system introduced in iOS 6. Listing 7-1 Storing Tab State to User Defaults implementation TestBedAppDelegate UITabBarController tabBarController - (void)tabBarController:(UITabBarController )tabBarController didEndCustomizingViewControllers:(NSArray )viewControllers changed:(BOOL)changed // Collect and store the view controller order NSMutableArray titles NSMutableArray array for (UIViewController vc in viewControllers) titles addObject:vc. title NSUserDefaults standardUserDefaults setObject:titles forKey:tabOrder NSUserDefaults standardUserDefaults synchronize - (void)tabBarController:(UITabBarController )controller didSelectViewController:(UIViewController )viewController // Store the selected tab NSNumber tabNumber NSNumber numberWithInt:controller selectedIndex NSUserDefaults standardUserDefaults setObject:tabNumber forKey:selectedTab NSUserDefaults standardUserDefaults synchronize - (BOOL)application:(UIApplication )application didFinis hLaunchingWithOptions:(NSDictionary )launchOptions // Globally use a black tint for nav bars UINavigationBar appearance setBarTintColor:UIColor blackColor NSMutableArray controllers NSMutableArray array NSArray titles NSUserDefaults standardUserDefaults objectForKey:tabOrder if (titles) // titles retrieved from user defaults for (NSString theTitle in titles) BrightnessController controller BrightnessController controllerWithBrightness: (theTitle intValue / 10) UINavigationController nav UINavigationController alloc initWithRootViewController:controller nav. navigationBar. barStyle UIBarStyleBlackTranslucent controllers addObject:nav else // generate all new controllers for (int i 0 i lt 10 i) BrightnessController controller BrightnessController controllerWithBrightness:i UINavigationController nav UINavigationController alloc initWithRootViewController:controller nav. navigationBar. barStyle UIBarStyleBlackTranslucent controllers addObject:nav tabBarController UITabBarControl ler alloc init tabBarController. tabBar. barTintColor UIColor blackColor tabBarController. tabBar. translucent NO tabBarController. viewControllers controllers tabBarController. customizableViewControllers controllers tabBarController. delegate self // Restore any previously selected tab NSNumber tabNumber NSUserDefaults standardUserDefaults objectForKey:selectedTab if (tabNumber) tabBarController. selectedIndex tabNumber intValue window UIWindow alloc initWithFrame:UIScreen mainScreen bounds window. tintColor COOKBOOKPURPLECOLOR tabBarController. edgesForExtendedLayout UIRectEdgeNone window. rootViewController tabBarController window makeKeyAndVisible return YES end Recipe: Page View Controllers The UIPageViewController class builds a book-like interface that uses individual view controllers as its pages. Users swipe from one page to the next or tap the edges to move to the next page or previous page. You can create a book-looking layout with pages, as shown in Figure 7-5 (left), or use a flat scrolling presentation, as shown in Figure 7-5 (right). The scrolling presentation offers an optional page indicator presentation, which is shown here at the bottom of the view. Figure 7-5 The UIPageViewController class creates virtual ldquobooksrdquo from individual view controllers. View your books in paged (left) or scrolling (right) presentations. All of a controllerrsquos pages can be laid out in a similar fashion, as in Figure 7-5 . or each page can provide a unique user interaction experience. Apple precooked all the animation and gesture handling into the class for you. You provide the content, implementing delegate and data source callbacks. Your code customizes a page view controllerrsquos look and behavior. Key properties specify how many pages display simultaneously, the content used for the reverse side of each page, and more. Herersquos a rundown of those Apple-specified properties: The transitionStyle property controls how one view controller transitions to the next. At this writing, the only transition styles supported by the page view controller are the page curl, as shown in Figure 7-5 (left), UIPageViewControllerTransitionStylePageCurl, and the scrolling presentation, UIPageViewControllerTransitionStyleScroll. This latter style was introduced in iOS 6. The controllerrsquos doubleSided property determines whether content appears on both sides of a page, as shown in Figure 7-5 (left), or just one side (right). Reserve the double-sided presentation for side-by-side layout when showing two pages simultaneously. If you donrsquot, yoursquoll end up making half your pages inaccessible. The controllers on the ldquobackrdquo of the pages will never move into the primary viewing space. The book layout is controlled by the bookrsquos spine. The spineLocation property can be set at the left or right, top or bottom, or center of the page. The three spine constants are UIPageViewControllerSpineLocationMin, corresponding to top or left, UIPageViewControllerSpineLocationMax for the right or bottom, and UIPageViewControllerSpineLocationMid for the center. The first two of these produce single-page presentations the last, with its middle spine, is used for two-page layouts. Return one of these choices from thepageViewController:spineLocationForInterfaceOrientation: delegate method, which is called whenever the device reorients, to let the controller update its views to match the current device orientation. Set the navigationOrientation property to specify whether the spine goes left/right or top/bottom. Use either UIPageViewControllerNavigationOrientationHorizontal (left/right) or UIPageViewControllerNavigationOrientationVertical(top/bottom). For a vertical book, the pages flip up and down rather than employing the left and right flips normally used. Wrapping the Implementation Like table views, a page view controller uses a delegate and data source to set the behavior and contents of its presentation. Unlike with table views, itrsquos simplest to wrap these items into a custom class to hide their details from applications. The code needed to support a page view implementation is rather quirkymdashbut highly reusable. A wrapper lets you turn your attention away from fussy coding details to specific content-handling concerns. In the standard implementation, the data source is responsible for providing page controllers on demand. It returns the next and previous view controllers in relationship to a given one. The delegate handles reorientation events and animation callbacks, setting the page view controllerrsquos controller array, which always consists of either one or two controllers, depending on the view layout. As Recipe 7-6 demonstrates, itrsquos a bit of a mess to implement, but once built, itrsquos something you really donrsquot need to spend much time coming back to. Recipe 7-6 creates a BookController class. This class numbers each page, hiding the next/previous implementation details and handling all reorientation events. A custom delegate protocol (BookDelegate) becomes responsible for returning a controller for a given page number when sent the viewControllerForPage: message. This simplifies implementation so that the calling app has only to handle a single method, which it can do by building controllers by hand or by pulling them from a storyboard. To use the class defined in Recipe 7-6 . you establish the controller, declare it as a child view controller, and add its view as a subview. Adding BookController as a child view controller ensures that it receives orientation and memory events. This type of view controller relationship will be discussed in more detail in the next recipe. Finally, the initial page number is set. Herersquos what that code might look like: - (void)viewDidLoad super viewDidLoad if (bookController) bookController BookController bookWithDelegate:self style:BookLayoutStyleBook bookController. view. frame self. view. bounds self addChildViewController:bookController self. view addSubview:bookController. view bookController didMoveToParentViewController:self The book controller creation convenience method also takes a second argument: a style. Recipe 7-6 allows developers to build four styles of books: a traditional book, a vertical book, and two scrolling styles: typedef enum BookLayoutStyleBook, // side by side in landscape BookLayoutStyleFlipBook, // side by side in portrait BookLayoutStyleHorizontalScroll, BookLayoutStyleVerticalScroll, BookLayoutStyle The standard book presents one page in portrait (spine vertical and to the left) and a side-by-side presentation in landscape (spine vertical in the middle). This corresponds to a standard Western-style book, with page movement going left to right. The ldquofliprdquo-style book uses a horizontal spine. In landscape mode, the spine is at the top, with one page shown at a time. In portrait, that extends to two pages, with the horizontal spine in the middle, halfway between top and bottom. The two scroll layouts allow you to scroll horizontally and vertically through individual pages. You cannot use multipage (side-by-side) layout with scrolling. The tear-down process in viewWillDisappear allows the book controller to retire from its superview: - (void)viewWillDisappear:(BOOL)animated super viewWillDisappear:animated bookController willMoveToParentViewController:nil bookController. view removeFromSuperview bookController removeFromParentViewController Exploring the Recipe Recipe 7-6 handles its delegate and data source duties by tagging each view controllerrsquos view with a page number. It uses this number to know exactly which page is presented at any time and to delegate another class, BookDelegate, to produce a view controller by index. The page controller itself always stores zero, one, or two pages in its view controller array. Zero pages means the controller has not yet been properly set up. One page is used for spine locations on the edge of the screen, two pages for a central spine. If the page count does not exactly match the spine setup, you will encounter a rather nasty runtime crash. The controllers presented in those pages are produced by the two data source methods, which implement the before and after callbacks. In the page controllerrsquos native implementation, controllers are defined strictly by their relationship to each other, not by an index. This recipe replaces those relationships with a simple number, asking its delegate for the page at a given index. Here, given the orientation, the useSideBySide: method determines where to place the spine and thus how many controllers show simultaneously. This implementation sets landscape as side by side and portrait as one page. You may want to change this for your applications. For example, you might use only one page on the iPhone, regardless of orientation, to enhance text readability. Recipe 7-6 allows both user - and application-based page control. Users can swipe and tap to new pages, or the application can send a moveToPage: request. This allows you to add external controls in addition to the page view controllerrsquos gesture recognizers. The direction that the page turns is set by comparing the new page number against the old. This recipe uses a Western-style page turn, where higher numbers are to the right and pages flip to the left. You may want to adjust this as needed for countries in the Middle East and Asia. Recipe 7-6 continually stores the current page to system defaults, so it can be recovered when the application is relaunched. It also notifies its delegate when the user has turned to a given page. Building a Presentation Index Page view controllersrsquo scrolling layouts allow you to add an optional index (utilizing a page control). Any book that uses the scrolling layout style (UIPageViewControllerTransitionStyleScroll) can implement two data source methods. iOS uses them to build the indicator at the bottom of the scrolling book that you saw in Figure 7-5 (right). As you can see from this snippet, the implementation since its inception is a bit wobbly: - (NSInteger)presentationIndexForPageViewController: (UIPageViewController )pageViewController // Slightly borked in iOS 6 amp 7 // return self currentPage return 0 - (NSInteger)presentationCountForPageViewController: (UIPageViewController )pageViewController if (bookDelegate ampamp bookDelegate respondsToSelector:selector(numberOfPages)) return bookDelegate numberOfPages Applersquos documentation states that presentationIndexForPageViewController should return the index of the selected item. Unfortunately, this leads to madness (and crashes). Returning 0 from the presentation index and the number of pages for the presentation count produces the most stable indicator. The page count used here is deferred to the bookrsquos delegate, via an optional method called numberOfPages. Note that you are not limited to a one-to-one correlation between your index and your page count and current page number. For a large book, you can imagine dividing this number down somewhat, so each page dot corresponds to 5 or 10 pages, showing progress through the book without an exact page correspondence. Apple enables you to access a page view controllerrsquos gesture recognizers to allow or disallow touch-based page turns based on a touchrsquos location on a page. Donrsquot do it. First, this approach is not valid for scroll-based controllers. Second, adding recognizer delegate methods tends to mess up app stability. Recipe 7-6 Creating a Page View Controller Wrapper // Define a custom delegate protocol for this wrapper class protocol BookControllerDelegate ltNSObjectgt - (id)viewControllerForPage:(NSInteger)pageNumber optional - (NSInteger)numberOfPages // for scrolling layouts - (void)bookControllerDidTurnToPage:(NSNumber )pageNumber end // A book controller wraps the page view controller interface BookController. UIPageViewController ltUIPageViewControllerDelegate, UIPageViewControllerDataSourcegt (instancetype)bookWithDelegate: (idltBookControllerDelegategt)theDelegate style:(BookLayoutStyle)aStyle - (void)moveToPage:(NSUInteger)requestedPage - (int)currentPage property (nonatomic, weak) id ltBookControllerDelegategt bookDelegate property (nonatomic, assign) NSUInteger pageNumber property (nonatomic) BookLayoutStyle layoutStyle end pragma mark - Book Controller implementation BookController pragma mark Utility // Page controllers are numbered using tags - (NSInteger)currentPage NSInteger pageCheck ((UIViewController )self. viewControllers objectAtIndex:0).view. tag return pageCheck pragma mark Presentation indices for page indicator (Data Source) - (NSInteger)presentationIndexForPageViewController: (UIPageViewController )pageViewController // Slightly borked in iOS 6 amp 7 // return self currentPage return 0 - (NSInteger)presentationCountForPageViewController: (UIPageViewController )pageView Controller if (bookDelegate ampamp bookDelegate respondsToSelector:selector(numberOfPages)) return bookDelegate numberOfPages pragma mark Page Handling // Update if youd rather use some other decision strategy - (BOOL)useSideBySide:(UIInterfaceOrientation)orientation BOOL isLandscape UIInterfaceOrientationIsLandscape(orientation) // Each layout style determines whether side by side is used switch (layoutStyle) case BookLayoutStyleHorizontalScroll: case BookLayoutStyleVerticalScroll: return NO case BookLayoutStyleFlipBook: return isLandscape default: return isLandscape // Update the current page, set defaults, call the delegate - (void)updatePageTo:(NSUInteger)newPageNumber pageNumber newPageNumber NSUserDefaults standardUserDefaults setInteger:pageNumber forKey:DEFAULTSBOOKPAGE NSUserDefaults standardUserDefaults synchronize SAFEPERFORMWITHARG(bookDelegate, selector(bookControllerDidTurnToPage:), (pageNumber)) // Request view controller from delegate - (UIViewController )controlle rAtPage:(NSInteger)aPageNumber if (bookDelegate ampamp bookDelegate respondsToSelector: selector(viewControllerForPage:)) UIViewController controller bookDelegate viewControllerForPage:aPageNumber controller. view. tag aPageNumber return controller return nil // Update interface to the given page - (void)fetchControllersForPage:(NSUInteger)requestedPage orientation:(UIInterfaceOrientation)orientation BOOL sideBySide self useSideBySide:orientation NSInteger numberOfPagesNeeded sideBySide. 2. 1 NSInteger currentCount self. viewControllers. count NSUInteger leftPage requestedPage if (sideBySide ampamp (leftPage 2)) leftPage floor(leftPage / 2) 2 // Only check against current page when count is appropriate if (currentCount ampamp (currentCount numberOfPagesNeeded)) if (pageNumber requestedPage) return if (pageNumber leftPage) return // Decide the prevailing direction, check new page against the old UIPageViewControllerNavigationDirection direction (requestedPage gt pageNumber) UIPageViewControllerNavigationDirectionForward : UIPageViewControllerNavigationDirectionReverse // Update the controllers, never adding a nil result NSMutableArray pageControllers NSMutableArray array SAFEADD(pageControllers, self controllerAtPage:leftPage) if (sideBySide) SAFEADD(pageControllers, self controllerAtPage:leftPage 1) self setViewControllers:pageControllers direction:direction animated:YES completion:nil self updatePageTo:leftPage // Entry point for external move request - (v oid)moveToPage:(NSUInteger)requestedPage // Thanks Dino Lupo self fetchControllersForPage:requestedPage orientation:(UIInterfaceOrientation) self. interfaceOrientation pragma mark Data Source - (UIViewController )pageViewController: (UIPageViewController )pageViewController viewControllerAfterViewController: (UIViewController )viewController self updatePageTo:pageNumber 1 return self controllerAtPage:(viewController. view. tag 1) - (UIViewController )pageViewController: (UIPageViewController )pageViewController viewControllerBeforeViewController: (UIViewController )viewController self updatePageTo:pageNumber - 1 return self controllerAtPage:(viewController. view. tag - 1) pragma mark Delegate Method - (UIPageViewControllerSpineLocation)pageViewController: (UIPageViewController )pageViewController spineLocationForInterfaceOrientation: (UIInterfaceOrientation)orientation // Always start with left or single page NSUInteger indexOfCurrentViewController 0 if (self. viewControllers. count) in dexOfCurrentViewController ((UIViewController )self. viewControllers objectAtIndex:0).view. tag self fetchControllersForPage:indexOfCurrentViewController orientation:orientation // Decide whether to present side by side BOOL sideBySide self useSideBySide:orientation self. doubleSided sideBySide UIPageViewControllerSpineLocation spineLocation sideBySide UIPageViewControllerSpineLocationMid : UIPageViewControllerSpineLocationMin return spineLocation // Return a new book controller (instancetype)bookWithDelegate:(id)theDelegate style:(BookLayoutStyle)aStyle // Determine orientation UIPageViewControllerNavigationOrientation orientation UIPageViewControllerNavigationOrientationHorizontal if ((aStyle BookLayoutStyleFlipBook) (aStyle BookLayoutStyleVerticalScroll)) orientation UIPageViewControllerNavigationOrientationVertical // Determine transitionStyle UIPageViewControllerTransitionStyle transitionStyle UIPageViewControllerTransitionStylePageCurl if ((aStyle BookLayoutStyleHorizo ntalScroll) (aStyle BookLayoutStyleVerticalScroll)) transitionStyle UIPageViewControllerTransitionStyleScroll // Pass options as a dictionary. Keys are spine location (curl) // and spacing between vcs (scroll). BookController bc BookController alloc initWithTransitionStyle:transitionStyle navigationOrientation:orientation options:nil bc. layoutStyle aStyle bc. dataSource bc bc. delegate bc bc. bookDelegate theDelegate Get This Recipersquos Code To find this recipersquos full sample project, point your browser to github/erica/iOS-7-Cookbook and go to the folder for Chapter 7. Recipe: Custom Containers Applersquos split view controller was groundbreaking in that it introduced the notion that more than one controller could live onscreen at a time. Until the split view, the rule was one controller with many views at a time. With the split view, several controllers coexist onscreen, all of them independently responding to orientation and memory events. Apple exposed this multiple-controller paradigm to developers in the iOS 5 SDK, allowing developers to design a parent controller and add child controllers to it. Events are passed from parent to child as needed. This allows you to build custom containers, outside the Apple-standard set of containers such as tab bar and navigation controllers. Recipe 7-7 builds a reusable container that can hold either one or two children. When loaded with two child view controllers, it lets you flip from one to the other and back. It has quite a lot of conditionality built in. Thatrsquos because it can be used as a standalone view controller, as a child view controller itself, and as a modal view controller. Imagine the following situations. As with a navigation controller, you can create this flip view controller directly and set it as your primary windowrsquos root view controller. In that case, it has no further relationship with any hierarchy. It merely manages its children. You can also use it as a child of some other container, such as in a tab bar controller presentation, a split view controller, and so forth. When used in that way, it acts as both a parent of its children and as a child of the container that holds it. Finally, you can present the controller directly. The flip view container must behave as a solid citizen in all these situations. The controller therefore has two tasks. First, it must manage its children using standard UIKit calls. Second, it must be aware of how it is participating in the view hierarchy. This recipe adds a navigation bar so a Done button becomes available to end users. Adding and Removing a Child View Controller In the simplest scenario, adding a child to a container controller takes three steps: 1. Call addChildViewController: on the parent and pass the child as the argument (for example, self addChildViewController:childvc). 2. Add the child controllerrsquos view as a subview (for example, self. view addSubview:childvc. view). 3. Call didMoveToParentViewController: on the child with the parent as its argument (for example, childvc didMoveToParentViewController:self). To remove a child view controller, the steps are almost (but not quite) mirrored: 1. Call willMoveToParentViewController: on the child, passing nil as the argument (for example, childvc willMoveToParentViewController:nil). 2. Remove the child controllerrsquos view (for example, childvc. view removeFromSuperview). 3. Call removeFromParentViewController on the child (for example, childvc removeFromParentViewController). Transitioning Between View Controllers UIKit offers a simple way to animate view features when you move from one child view controller to another. You provide a source view controller, a destination, and a duration for the animated transition. You can specify the kind of transition in the options. Supported transitions include page curls, dissolves, and flips. This method creates a simple curl from one view controller to the next: - (void)action:(id)sender redController willMoveToParentViewController:nil self addChildViewController:blueController self transitionFromViewController:redController toViewController:blueController duration:1.0f options:UIViewAnimationOptionLayoutSubviews UIViewAnimationOptionTransitionCurlUp animations:(void) completion:(BOOL finished) redController. view removeFromSuperview self. view addSubview:blueController. view redController removeFromParentViewController blueController didMoveToParentViewController:self You can use the same approach to animate UIView properties without the built-in transitions. For example, this method re-centers and fades out the red controller while fading in the blue. These are all animatable UIView features and are changed in the animations: block: - (void)action:(id)sender redController willMoveToParentViewController:nil self addChildViewController:blueController blueController. view. alpha 0.0f self transitionFromViewController:redController toViewController:blueController duration:2.0f options:UIViewAnimationOptionLayoutSubviews animations:(void) redController. view. center CGPointMake(0.0f, 0.0f) redController. view. alpha 0.0f blueController. view. alpha 1.0f completion:(BOOL finished) redController. view removeFromSuperview self. view addSubview:blueController. view redController removeFromParentViewController blueController didMoveToParentViewController:self Using transitions and view animations is an either/or scenario. Either set a transition option or change view features in the animations block. Otherwise, they conflict, as you can easily confirm for yourself. Use the completion block to remove the old view and move the new view into place. Although simple to implement, this kind of transition is not meant for use with Core Animation. If you want to add Core Animation effects to your view-controller-to-view-controller transitions, think about using a custom segue instead. Segues are covered in the following recipe. As mentioned in Recipe 7-2 . a third option is available for animating transitions between UIViewControllers in iOS 7: The custom transitions API allows you to create advanced animations that can even interact dynamically with the user. Recipe 7-7 Creating a View Controller Container - (void)viewDidDisappear:(BOOL)animated super viewDidDisappear:animated if (controllers. count) NSLog(Error: No root view controller) return // Clean up the child view controller UIViewController currentController (UIViewController )controllers0 currentController willMoveToParentViewController:nil currentController. view removeFromSuperview currentController removeFromParentViewController - (void)flip:(id)sender // Please call only with two controllers if (controllers. count lt 2) return // Determine which item is front, which is back UIViewController front (UIViewController )controllers0 UIViewController back (UIViewController )controllers1 // Select the transition direction UIViewAnimationOptions transition reversedOrder UIViewAnimationOptionTransitionFlipFromLeft : UIViewAnimationOptionTransitionFlipFromRight // Hide the info button until after the flip infoButton. alpha 0.0f // Prepare the front for removal, the back for adding fr ont willMoveToParentViewController:nil self addChildViewController:back // Perform the transition self transitionFromViewController: front toViewController:back duration:0.5f options:transition animations:nil completion:(BOOL done) // Bring the Info button back into view self. view bringSubviewToFront:infoButton UIView animateWithDuration:0.3f animations:() infoButton. alpha 1.0f // Finish up transition front removeFromParentViewController back didMoveToParentViewController:self reversedOrder reversedOrder controllers back, front - (void)viewWillAppear:(BOOL)animated super viewWillAppear:animated if (controllers. count) NSLog(Error: No root view controller) return UIViewController front controllers0 UIViewController back nil if (controllers. count gt 1) back controllers1 self addChildViewController:front self. view addSubview:front. view front didMoveToParentViewController:self // Check for presentation and for flippability BOOL isPresented self. isBeingPresented // Clean up instan ce if re-use if (navbar infoButton) navbar removeFromSuperview infoButton removeFromSuperview navbar nil // When presented, add a custom navigation bar. // iPhone navbar height must consider status bar. CGFloat navbarHeight ISIPHONE. 64.0. 44.0 if (isPresented) navbar UINavigationBar alloc init self. view addSubview:navbar PREPCONSTRAINTS(navbar) ALIGNVIEWTOP(self. view, navbar) ALIGNVIEWLEFT(self. view, navbar) ALIGNVIEWRIGHT(self. view, navbar) CONSTRAINHEIGHT(navbar, navbarHeight) // Right button is Done when VC is presented self. navigationItem. leftBarButtonItem nil self. navigationItem. rightBarButtonItem isPresented SYSBARBUTTON(UIBarButtonSystemItemDone, selector(done:)). nil // Populate the navigation bar if (navbar) navbar setItems:self. navigationItem animated:NO // Size the child VC view(s) CGFloat verticalOffset (navbar nil). navbarHeight. 0.0f CGRect destFrame CGRectMake(0.0f, verticalOffset, self. view. frame. size. width, self. view. frame. size. height - verticalOffset) front. view. frame destFrame back. view. frame destFrame // Set up info button if (controllers. count lt 2) return // our work is done here // Create the i button infoButton UIButton buttonWithType:UIButtonTypeInfoLight infoButton. tintColor UIColor whiteColor infoButton addTarget:self action:selector(flip:) forControlEvents:UIControlEventTouchUpInside // Place i button at bottom right of view self. view addSubview:infoButton PREPCONSTRAINTS(infoButton) ALIGNVIEWRIGHTCONSTANT(self. view, infoButton, - infoButton. frame. size. width) ALIGNVIEWBOTTOMCONSTANT(self. view, infoButton, - infoButton. frame. size. height) end Get This Recipersquos Code To find this recipersquos full sample project, point your browser to github/erica/iOS-7-Cookbook and go to the folder for Chapter 7. When you use storyboards, IB provides a set of standard segues to transition between your view c ontrollers. With custom containers come their little brother, custom segues. Just as tab and navigation controllers provide a distinct way of transitioning between child view controllers, you can build custom segues that define transition animations unique to your class. IB doesnrsquot provide a lot of support for custom containers with custom segues, so itrsquos best to develop your segue presentations in code for now. Herersquos how you might implement code to move a view controller to a new view: // Informal custom delegate method - (void)segueDidComplete // Retrieve the two vcs UIViewController source childControllers objectAtIndex:vcIndex UIViewController destination childControllers objectAtIndex:nextIndex // Reparent as needed destination didMoveToParentViewController:self source removeFromParentViewController // Update the bookkeeping vcIndex nextIndex pageControl. currentPage vcIndex // Transition to new view using custom segue - (void)switchToView:(int)newIndex goingForward:(BOOL)goesForward if (vcIndex newIndex) return nextIndex newIndex // Segue to the new controller UIViewController source childControllers objectAtIndex:vcIndex UIViewController destination childControllers objectAtIndex:newIndex // Start the reparenting process source willMoveToParentViewController:nil self addChildViewController:destination RotatingSegue segue RotatingSegue alloc initWithIdentifier:segue source: source destination:destination segue. goesForward goesForward segue. delegate self segue perform Here, the code identifies the source and destination child controllers, builds a segue, sets its parameters, and tells it to perform. An informal delegate method is called back by that custom segue on its completion. Recipe 7-8 shows how the segue is built. In this example, it creates a rotating cube effect that moves from one view to the next. Figure 7-6 shows the segue in action. Figure 7-6 Custom segues allow you to create visual metaphors for your custom containers. Recipe 7-8 builds a ldquocuberdquo of view controllers that can be rotated from one to the next. The switches on each controller update the art alpha value from translucent to solid and back. The seguersquos goesForward property determines whether the rotation moves to the right or left around the virtual cube. Although this example uses four view controllers, as you saw in the code that laid out the child view controllers, thatrsquos a limitation of the metaphor, not of the code itself, which will work with any number of child controllers. You can just as easily build three - or seven-sided presentations with this, although you are breaking an implicit ldquorealityrdquo contract with your user if you do so. To add more (or fewer) sides, you should adjust the animation geometry in the segue away from a cube to fit your virtual n - hedron. Recipe 7-8 Creating a Custom View Controller Segue implementation RotatingSegue CALayer transformationLayer UIView weak hostView // Return a shot of the given view - (UIImage )screenShot:(UIView )aView // Arbitrarily dims to 40. Adjust as desired. UIGraphicsBeginImageContext(hostView. frame. size) aView. layer renderInContext:UIGraphicsGetCurrentContext() UIImage image UIGraphicsGetImageFromCurrentImageContext() CGContextSetRGBFillColor(UIGraphicsGetCurrentContext(), 0, 0, 0, 0.4f) CGContextFillRect(UIGraphicsGetCurrentContext(), hostView. frame) UIGraphicsEndImageContext() return image // Return a layer with the view contents - (CALayer )createLayerFromView:(UIView )aView transform:(CATransform3D)transform CALayer imageLayer CALayer layer imageLayer. anchorPoint CGPointMake(1.0f, 1.0f) imageLayer. frame (CGRect) imageLayer. transform transform UIImage shot self screenShot:aView imageLayer. contents (bridge id) shot. CGImage // On starting the animation, remove the source view - (void)animationDidStart:(CAAnimation )animation UIViewController source (UIViewController ) super. sourceViewController source. view removeFromSuperview // On completing the animation, add the destination view, // remove the animation, and ping the deleg ate - (void)animationDidStop:(CAAnimation )animation finished:(BOOL)finished UIViewController dest (UIViewController ) super. destinationViewController hostView addSubview:dest. view transformationLayer removeFromSuperlayer if (delegate ampamp delegate respondsToSelector: selector(segueDidComplete)) delegate segueDidComplete // Perform the animation -(void)animateWithDuration:(CGFloat)aDuration CAAnimationGroup group CAAnimationGroup animation group. delegate self group. duration aDuration CGFloat halfWidth hostView. frame. size. width / 2.0f float multiplier goesForward. -1.0f. 1.0f // Set the x, y, and z animations CABasicAnimation translationX CABasicAnimation animationWithKeyPath:sublayerTransform. translation. x translationX. toValue NSNumber numberWithFloat:multiplier halfWidth CABasicAnimation translationZ CABasicAnimation animationWithKeyPath:sublayerTransform. translation. z translationZ. toValue NSNumber numberWithFloat:-halfWidth CABasicAnimation rotationY CABasicAnimation animationWithKeyPath:sublayerTransform. rotation. y rotationY. toValue NSNumber numberWithFloat: multiplier MPI2 // Set the animation group group. animations NSArray arrayWithObjects: rotationY, translationX, translationZ, nil group. fillMode kCAFillModeForwards group. removedOnCompletion NO // Perform the animation CATransaction flush transformationLayer addAnimation:group forKey:kAnimationKey - (void)constructRotationLayer UIViewController source (UIViewController ) super. sourceViewController UIViewController dest (UIViewController ) super. destinationViewController hostView s ource. view. superview // Build a new layer for the transformation transformationLayer CALayer layer transformationLayer. frame hostView. bounds transformationLayer. anchorPoint CGPointMake(0.5f, 0.5f) CATransform3D sublayerTransform CATransform3DIdentity sublayerTransform. m34 1.0 / -1000 transformationLayer setSublayerTransform:sublayerTransform hostView. layer addSublayer:transformationLayer // Add the source view, which is in front CATransform3D transform CATransform3DMakeIdentity transformationLayer addSublayer: self createLayerFromView:source. view transform:transform // Prepare the destination view either to the right or left // at a 90/270 degree angle off the main transform CATransform3DRotate(transform, MPI2, 0, 1, 0) transform CATransform3DTranslate(transform, hostView. frame. size. width, 0, 0) if (goesForward) transform CATransform3DRotate(transform, MPI2, 0, 1, 0) transform CATransform3DTranslate(transform, hostView. frame. size. width, 0, 0) transform CATransform3DRotate(tr ansform, MPI2, 0, 1, 0) transform CATransform3DTranslate(transform, hostView. frame. size. width, 0, 0) transformationLayer addSublayer: self createLayerFromView:dest. view transform:transform // Standard UIStoryboardSegue perform - (void)perform self constructRotationLayer self animateWithDuration:0.5f end Get This Recipersquos Code To find this recipersquos full sample project, point your browser to github/erica/iOS-7-Cookbook and go to the folder for Chapter 7. Starting in the iOS 6 SDK, you can apply custom segues in your storyboards. Yoursquoll need to tie those segues to some action item, such as a button or bar button press, or similar actionable element. Figure 7-7 shows how custom segues are listed in IB. The ldquorotatingrdquo segue is from Recipe 7-8 . Figure 7-7 Storyboards allow you to apply custom segues in IB. IB scans for UIStoryboardSegue child classes. Here, IB lists the custom ldquorotatingrdquo segue along with system-supplied options. Whatrsquos more, segues can be ldquounwound. rdquo Unwinding allows you to move back from a new view controller to its logical parent, using a custom segue you provide. You achieve this by implementing a few methods: Specify whether you can unwind with canPerformUnwindSegueAction:fromViewController:withSender:. Return a view controller to viewControllerForUnwindSegueAction:fromViewController:withSender. This controller will be the unwinding destination. Supply the required unwinding segue instance via segueForUnwindingToView-Controller:fromViewController:identifier. Typically, yoursquoll want your unwind to animate in the reverse direction from your original segue. Finally, you can now allow or disallow any segue by implementing shouldPerformSegue-WithIdentifier:sender. You return either YES or NO, depending on whether you want the identified segue to proceed. This chapter shows many view controller classes in action. Yoursquove learned how to use them to handle view presentation and user navigation for various device deployment choices. With these classes, you have discovered how to expand virtual interaction space and create multipage interfaces, as demanded by applications, while respecting the HIG on the platform in question. Before moving on to the next chapter, here are a few points to consider about view controllers: Use navigation trees to build hierarchical interfaces. They work well for looking at file structures or building a settings tree. When you think ldquodisclosure viewrdquo or ldquopreferences, rdquo consider pushing a new controller onto a navigation stack or using a split view to present them directly. Donrsquot be afraid to use conventional UI elements in unconventional ways, as long as you respect the overall Apple HIG. You can apply innovative approaches for UINavigationController that donrsquot involve any navigation. The tools are there for you to use. Be persistent. Let your users return to the same GUI state that they last left from. NSUserDefaults provides a built-in system for storing information between application runs. Use these defaults to re-create the prior interface state. The State Preservation and Restoration API introduced in iOS 6 provides another path for persisting large portions of your UI state. Go universal. Let your code adapt itself for various device deployments rather than force your app into an only-iPhone or only-iPad design. This chapter touches on some simple runtime device detection and interface updates that you can easily expand for more challenging circumstances. Universal deployment isnrsquot just about stretching views and using alternate art and XIB files. Itrsquos also about detecting when a device influences the way you interact in addition to the look of the interface. When working with custom containers, donrsquot be afraid of using storyboards directly. You do not have to build and retain an array of all your controllers simultaneously. Storyboards offer direct access to all your elements. As with the new page view controller class, just load the controllers you need, when you need them.

Комментариев нет:

Отправить комментарий