Thursday 13 July 2017

Arestas para o ângulo de referência


Assim exemplo temos classe Vamos olhar primeiro em cópia rasa (padrão usado em iOS) copiar variável terá copiado referência de referência, mas ambas as referências de ambas as variáveis ​​(cópia e instância) estará apontando para um (mesmo) objeto de memória - o que significa mudança de referência Em uma instância levará a alteração em outra (mesmo para ambos), mas se vamos realocar (copy. reference NSObject novo) ele irá realocar apenas para copiar variável e, por exemplo, será a anterior. Assim, todos juntos - copiando apenas referências, mas não a memória que estão apontando (será o mesmo para ambas as referências) Deep copiar se comportando de outra forma - se você estiver copiando objetos ele copiará referências e cada referência copiada estará apontando para a sua Próprio objeto de memória copiado. Isso significa que mudar um objeto não vai levar a mudar o outro como eles são copiados com referências (não como anterior) e são brevemente alocados separadamente na memória. Assim, todos juntos - copiar objetos levará a copiar referências e objetos que estão apontando. É por isso que é cópia profunda - copia tudo e não apenas referências. Acima eu adicionei imagens de cópia rasa e profunda para uma melhor compreensão. Primeiro é superficial e segundo é deep. UICollectionView executeBatchUpdates conclusão não chamado enquanto fora da tela Im usando o teste de instantâneo para o meu controlador de visualização. Isso é como o controlador de exibição é inicializado em testes: Meu controlador de exibição contém UICollectionView. Quando executo atualizações de exibição de coleção usando performBatchUpdates, mesmo que o bloco de atualização está concluído, a conclusão nunca é chamada. Eu acho que está relacionado com a renderização fora da tela da exibição de coleção. Alguém tem alguma experiência com problema semelhante O que estou faltando para convencer UICollectionView que o seu na tela eu encontrei o problema. Era tudo sobre um timing adequado. O caso de teste terminou antes da conclusão foi chamado eo controlador de visão foi desalocado. Eu acelerei as animações de exibição de coleção por configuração e aumentou o tempo limite para o caso de teste para 0,1 segundos. Tudo funciona como esperado agora. Veja também perguntas próximas a este tópico Como faço para imprimir o valor de NSErrorFailingURLStringKey em swift para um UIWebViews didFailLoadWithError Eu tenho localizedDescription e outras opções para NSError, mas procurando NSErrorFailingURLStringKey Cadeia. Isso é possível, por favor ajude. Desde já, obrigado. Eu quero fazer um aplicativo simples que irá mostrar um artigo aleatório de uma lista de leitura de usuários safari, mas não conseguia encontrar uma maneira fácil de fazer isso, é possível Obrigado antecipadamente. Eu tenho página de detalhes de notícias com imagem, título e descrição, minha descrição é UITextView e eu tenho 2 UIButtons. A e A - Eu quero fazer quando o usuário clicar Um botão irá mudar o meu tamanho de fonte UITextView. Corrente 1. E quando o usuário clicar em um botão irá mudar o meu tamanho de fonte UITextView. Current -1 Eu tento com sob códigos abaixo, mas não fazer nenhuma ação quando os botões clicaram Meu UItextview. NSAttributedString códigos abaixo eu preciso exibir uma seqüência de caracteres no meu UICollectionViewCell. Criei uma célula de exibição personalizada. Mas sempre que eu executar o aplicativo mostra erro fatal: inesperadamente encontrado nil enquanto desembrulhando um valor opcional Eu tenho um TableView e em que UITableViewCell Eu tenho um UICollectionView. Agora em minha collectionView Para cada collectionViewCell há uma opção criar célula e remover célula botão. Então, para criar a célula eu tenho que ir para CreateViewController e para apagar o botão eu tenho que apagar respectiva collectionViewCell. Agora, no meu didSelectRow método if (indexPath. row 0), então eu tenho que ir para CreateViewController. Como eu posso fazer isso. Eu sou novo no delegado também. Por favor, ajude. Eu estou trabalhando atualmente em um vídeo que joga a aplicação de Objective-C em Xcode que eu configurei atualmente para indicar pilhas de miniaturas video em uma vista da coleção e quando as miniaturas são selecionadas, os videos abrem acima em uma cena diferente que carregue e jogue o selecionado vídeo. Cada célula na exibição de coleção é composta de uma miniatura de vídeo Exibição de imagem. 2 etiquetas. E 2 botões e quando a miniatura é tocada, uma nova cena é lançada que reproduz o vídeo em um player de vídeo externo que eu comprei uma licença para e ter implementado em meu aplicativo. Uma captura de tela deste aplicativo pode ser vista abaixo. Agora eu quero substituir a funcionalidade atual de exibir uma miniatura de Exibição de Imagem que lança uma Cena diferente para reproduzir o vídeo, para poder reproduzir o vídeo no lugar da miniatura diretamente na Célula de Exibição de Coleção (A maneira como o Instagram reproduz seus vídeos) Com o leitor de vídeo externo. Para fazer isso, eu acredito que o meu primeiro passo seria determinar qual Cell na Collection View está ocupando a maioria da tela sempre que o usuário pára de rolar (neste screenshot, a célula que está ocupando a maioria da tela É a foto do grupo de pessoas). Existe uma maneira de calcular qual Cell está ocupando a maioria da tela visível toda vez que um usuário pára de rolagem e, em seguida, chamar funções naquela célula uma vez que é determinado que estou usando uikit para upload. Para upload inicial de seus trabalhos bem. Mas quando eu quero carregar o arquivo no local do arquivo atual, então não está acontecendo. Eu quero dizer em duplo clique eu estou definindo o caminho de localização atual para uma entrada e quando o arquivo é selecionado, então eu quero trabalhar com o método UIkit. uploadSelect () com o caminho, mas não é obtê-lo. Talvez porque é uma função anônima e chamadas on dom load ou page refresh. Há alguma maneira de fazer isso. Eu estou preso a partir de 2 dias Em meu viewDidLoad estou chamando método: No iphone 5 estou recebendo esta saída: Como corrigir este problema de gradiente mover para cima no iphone 6s mais eu tenho um UILabel dentro de uma vista com altura variável. Eu quero o rótulo para embrulhar uma vez que a altura é suficiente para 2 linhas. Esta imagem mostra a etiqueta com uma altura pequena, o texto está em 1 linha como necessário. É fixado às superfícies superiores, esquerdas e inferiores do superviews. No momento, se eu aumentar a altura, então ele se parece com isso: Mas eu gostaria que ele ir mais de 2 linhas, pois agora há espaço suficiente: Se eu adicionar uma nova linha n para a minha cadeia entre a data ea hora, simplesmente corta o 2ª linha quando não houver altura suficiente. Se eu restringir a largura para forçar o envolvimento, ele trunca o texto quando ele deve ser exibido em uma única linha. Definir preferredMaxLayoutWidth se comporta da mesma forma que restringir a largura. Eu também quero evitar definir a largura da etiqueta como o texto eo tamanho da fonte pode variar, idealmente, este comportamento seria baseado na altura apenas. Definir ContentCompressionResistancePriority e ContentHuggingPriority também não parecem fazer qualquer diferença. Isso é realmente possível com autolayout, ou eu preciso verificar a altura manualmente e forçar wrapping Eu tenho testes de instantâneo que são por dispositivo. Eu quero verificar em meus testes que estou executando em um simulador particular que tem um instantâneo para ele. Então, por exemplo, eu quero testar que o simulador atual é iPhone6sPlus9.2 desde o snapshot wasnt gravado para os muitos outros tipos de simulador que eu tenho. Eu tentei muitas variações como: eo código de mas ambos estão retornando valores irrelevantes como x86 para o tipo de dispositivo. Pergunta: Para esta solicitação pull. Os testes não passam no CircleCl, mas os testes passam localmente. Por que a saída de teste CircleCI mostra falhas para todos os testes FBSnapshotTestCase. Por exemplo: testAdjustsFontSizeToFitWidth, ((comparisonSuccess) is true) falhou - Falha na comparação de instantâneos: Erro DomainFBSnapshotTestControllerErrorDomain Code1 Não foi possível carregar a imagem de referência. UserInfo0x7f85f36b0a50 Imagem de referência não encontrada. Você precisa executar o teste no modo de gravação. NSLocalizedDescriptionUnable para carregar imagem de referência. FBReferenceImageFilePathKey / Users / distiller / TTTAttributedLabel / Exemplo / TTTAttributedLabelTests / ReferenceImages32 / TTTAttributedLabelTests / testAdjustsFontSizeToFitWidth2x. png testAttributedTruncationToken, ((comparisonSuccess) é true) falhou - Falha na comparação de instantâneos: Erro DomainFBSnapshotTestControllerErrorDomain Code1 Não foi possível carregar a imagem de referência. UserInfo0x7f85f35b06d0 Imagem de referência não encontrada. Você precisa executar o teste no modo de gravação. NSLocalizedDescriptionUnable para carregar imagem de referência. No entanto, os mesmos testes passam localmente: Em CircleCI os testes usam o diretório ReferenceImages32, que não existe: No entanto, as imagens não existem em ReferenceImages64. Voltar para o início Submeter comentários Propriedades ID do artigo: 815500 - Última revisão: quarta-feira, Espero que o diretório 64 seja usado porque o arquivo circle. yml especifica o uso do simulador do iPhone 6: Capítulo 7. Trabalhando com Controladores de Exibição Os controladores de exibição simplificam o gerenciamento de exibição para muitas aplicações iOS. Cada controlador de visão possui uma hierarquia de visualizações, que apresenta um elemento completo de uma interface unificada. Os controladores de visualização permitem-lhe criar aplicações que centralizam muitas tarefas, incluindo alterações de orientação e respostas às acções do utilizador. Este capítulo analisa o uso de classes controlanddashbased view e como aplicá-las a situações do mundo real para ambos os cenários de projeto do iPhone / iPod e do iPad. Como seu nome sugere, os controladores de exibição fornecem o componente de controlador do padrão de projeto iOSrsquos ModelndashViewndashController. Cada controlador de vista gere um conjunto de vistas que compreendem um único componente de interface de utilizador dentro de uma aplicação. Os controladores de visualização coordenam o carregamento e a aparência da visualização, bem como participam na resposta às interações do usuário. Os controladores de visualização também se harmonizam com o dispositivo eo sistema operacional subjacente. Quando um usuário gira o dispositivo, por exemplo, o controlador de visualização pode atualizar seu layout viewsrsquo. Quando o sistema operacional encontra um cenário de baixa memória, os controladores respondem aos avisos de memória. Em resumo, os controladores de visualização fornecem gerenciamento central. Eles negociam com uma série de requisitos de desenvolvimento ortogonal, obtidos de vistas, modelos, iOS e do próprio dispositivo. Os controladores de visualização também centralizam metáforas de apresentação. A capacidade de visualizar controladores de camada em contêineres estende o paradigma a projetos customizados excitantes. 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 sua atenção de exibição para exibição, controladores de exibição de página que apresentam livros virtuais, controladores de guias que oferecem acesso por botão de acesso a vários controladores filho e vista dividida Controladores que oferecem apresentações de mestre-lista / detalhe. Exibir controladores arenrsquot views. São classes sem representação visual, exceto pelas visualizações que gerem. Os controladores de exibição ajudam seus pontos de vista a viver em um ambiente de projeto de aplicativos maior. O iOS SDK oferece muitas classes de controlador de visualização. Essas classes variam de geral a específico. Herersquos um guia rápido para um subconjunto dos controladores de vista yoursquoll encontro ao construir suas interfaces de aplicativo iOS. UIViewController é a classe pai para os controladores de vista e aquele que você usa para gerenciar suas visualizações primárias. É o cavalo de batalha de controladores de visão. Você pode gastar uma grande parte do seu tempo personalizando subclasses dessa classe. A classe básica UIViewController gerencia cada tempo de vida principal do viewrsquos do início ao fim e leva em conta as mudanças que a visão deve reagir ao longo do caminho. As instâncias do UIViewController são responsáveis ​​por configurar o aspecto de uma vista e quais subviews ele exibe. Muitas vezes eles dependem de carregar essa informação de XIB ou arquivos de storyboard. Os métodos de instância permitem que você crie manualmente o layout de exibição em código (loadView) ou adicione comportamento depois que uma visualização terminar de carregar (viewDidLoad). Reagir a exibições exibidas ou descartadas é outro trabalho que os controladores de exibição manipulam. Estas são as realidades de pertencer a uma aplicação maior. Métodos como viewWillAppear: e viewWillDisappear: permitem concluir qualquer contabilidade associada ao seu gerenciamento de exibição. Você pode pré-carregar dados antes de ser apresentado ou limpar uma vez que uma exibição não será mais exibida na tela. Cada uma das tarefas mencionadas aqui especifica como uma exibição se encaixa em uma aplicação envolvente. O UIViewController medeia entre as visões e essas demandas externas, permitindo que a visão se alterasse para atender a essas necessidades. Como o nome sugere, os controladores de navegação permitem que você drill para cima e para baixo através de hierarquias de exibição baseada em árvore, que é uma importante estratégia de design de interface primária em membros menores da família de dispositivos iOS e um suporte em tablets. Os controladores de navegação criam as barras de navegação translúcidas que aparecem na parte superior de muitas aplicações iOS padrão. Os controladores de navegação permitem que você empurre novas vistas para o lugar em uma pilha armazenada e automaticamente gerar botões Voltar que mostram o título do controlador de exibição de chamada. Todos os controladores de navegação usam um controlador de visualização ldquorootrdquo para estabelecer o topo de sua árvore de navegação, permitindo que os botões Back voltem à vista principal. Nos tablets, você pode usar uma interface de navegação baseada em controlerndash para trabalhar com itens de menu barra buttonndashbased, para apresentar apresentações popover, ou para integrar com instâncias UISplitViewController para uma experiência de apresentação mestre / detalhe. A entrega de responsabilidade a um controlador de navegação permite-lhe concentrar o trabalho de concepção na criação de ecrãs de controladores de visualização individuais. Você donrsquot tem que se preocupar com detalhes de navegação específica diferente de dizer ao controlador de navegação que vista para mover para o próximo. A pilha de histórico e os botões de navegação são tratados para você. Controladores de Barras de Guia A classe UITabBarController permite controlar apresentações paralelas em seu aplicativo. Estes são como estações em um rádio. Uma barra de tabulação ajuda os usuários a selecionar o controlador de exibição para ldquotune, rdquo 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ê cria o controlador de visualização ou o controlador de navegação que habita cada guia, e o Cocoa Touch manipula os vários detalhes da visualização. Por exemplo, quando as instâncias de barra de guia oferecem mais do que um certo número de opções de controlador de visualização de cada vez (cinco na família de dispositivos do iPhone, mais em tablets), os usuários podem personalizá-las através da tela Mais. A tela Mais gt Editar permite que os usuários arrastem seus controladores favoritos até a barra de botões na parte inferior da tela. Nenhuma programação extra está envolvida. Você ganha guias editáveis ​​de graça. Tudo o que você precisa fazer é solicitá-los através da propriedade customizableViewControllers. Controladores de Exibição Dividida Sugeridos para uso em aplicativos de tablet, a classe UISplitViewController oferece uma forma 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 email iPadrsquos. Quando usado na orientação horizontal, uma lista de mensagens aparece no lado esquerdo do conteúdo da mensagem individual à direita. A exibição de detalhes (o conteúdo da mensagem no Mail) à direita é subordinada à exibição mestre (lista de mensagens Mailrsquos) à esquerda. Tocar em uma mensagem atualiza a visualização do lado direito com seu conteúdo. Na orientação vertical, a vista principal normalmente está oculta. É acessado através de um popover, que é alcançado tocando no botão esquerdo do split viewrsquos top bar ou através de um gesto swipe (no iOS 5.1 e posterior). Controladores de exibição de página Como controladores de navegação, controladores de exibição de guia e controladores de exibição de divisão, os controladores de exibição de página são recipientes 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 da página, você define o bookrsquos ldquospine, rdquo tipicamente ao longo da esquerda ou parte superior da vista. Crie seu ldquobookrdquo adicionando controladores de exibição de conteúdo individual. Cada ldquopagerdquo transições para o próximo usando página curls ou panelas. Especificamente para tablets, os controladores popover criam visões transitórias que surgem sobre outros conteúdos de interface existentes. Esses controladores apresentam informações sem assumir a tela inteira, da maneira que modos modos normalmente fazem. Os popovers são geralmente invocados tocando 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 exibiçã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 popoverrsquos contentViewController antes de apresentar o popover. Isso permite popovers para apresentar qualquer intervalo de material que você pode projetar em um controlador de exibição padrão, oferecendo flexibilidade de programação excepcional. Começando no iOS 5, você pode subclasse UINavigationBar e incorporar apresentações personalizadas em suas interfaces de navegação apprsquos. Use o initWithNavigationBarClass: toolbarClass: método de inicialização. Desenvolvendo com controladores de navegação e vistas divididas A classe UINavigationController oferece uma das formas mais importantes de gerenciar interfaces em um dispositivo com espaço de tela limitado. Ele cria uma maneira para os usuários navegarem para cima e para baixo uma hierarquia de apresentações de interface para criar uma GUI virtual thatrsquos muito maior do que o dispositivo. Os controladores de navegação dobram suas GUIs em um esquema baseado em árvore puro. Usuários viajam através desse esquema usando botões e escolhas que os transportam ao redor da árvore. Você vê os controladores de navegação no aplicativo Contatos e em Configurações, onde as seleções levam a novas telas e os botões Voltar mudam para os anteriores. Vários elementos GUI padrão revelam o uso de controladores de navegação em aplicações, como mostrado na Figura 7-1 (à esquerda). Estes incluem as suas grandes barras de navegação que aparecem no topo de cada ecrã, o botão de retrocesso no canto superior esquerdo que aparece quando o utilizador treina em hierarquias e os botões de opção no canto superior direito que oferecem outra funcionalidade de aplicação, tal como a edição. Muitas aplicações de controlador de navegação são construídas em torno de listas de deslocamento, onde os elementos de uma lista conduzem a novas telas, indicadas pelo indicador de divulgação (chevron cinzento) eo botão de divulgação de detalhe (rodeado i) encontrado no lado direito de cada célula da tabela. Figura 7-1 O controlador de navegação iPhonersquos (à esquerda) usa vigas cinzentas para indicar que as visualizações de detalhes serão pressionadas na tela quando seus pais forem selecionados. No iPad (direito), os controladores de vista dividida usam toda a tela, separando os elementos de navegação das apresentações de detalhes. O iPad, com seu tamanho de tela grande, não requer o tipo de atalhos de economia de espaço que os controladores de navegação utilizam nos dispositivos da família do iPhone. Os aplicativos Tablet podem usar controladores de navegação diretamente, mas o UISplitViewController mostrado na Figura 7-1 (à direita) oferece uma apresentação thatrsquos mais adequada para o dispositivo mais expansivo. Observe as diferenças entre a implementação do iPhone à esquerda ea implementação do iPad à direita da Figura 7-1. O controlador de vista dividida iPadrsquos não contém chevrons. Quando os itens são tocados, seus dados aparecem na mesma tela, usando a grande área de detalhes do lado direito. O iPhone, faltando este espaço, apresenta chevrons que indicam que as vistas novas serão empurradas na tela. Cada abordagem leva em consideração o design específico do dispositivo em sua apresentação. Ambas as vistas da Caixa de entrada do iPad e da família do iPhone usam elementos semelhantes do controlador de navegação. Estes incluem o botão Voltar (lt iCloud), um botão de opções (Editar) ea 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 e-mail 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 iPhone-família padrão usa chevrons para indicar folhas. Quando selecionados, esses controladores de exibição de folha são empurrados para a pilha de navegação. Eles se juntam aos outros controladores de viewrsquos que traçam um progresso de userrsquos através da interface. O iPad não empurra suas folhas. Ele os apresenta em uma visão separada e omite vigas que de outra forma indicam que os usuários atingiram a extensão da hierarquia traversal. Os controladores de navegação em estilo iPhone desempenham papéis também no iPad. Quando os aplicativos iPad usam controladores de navegação padrão (estilo iPhone), eles costumam fazê-lo em contextos estreitos, como apresentações de pop-ups transientes, onde o controlador é apresentado na tela em uma visão pequena com uma vida útil limitada. Caso contrário, os aplicativos iPad são incentivados a usar a abordagem de exibição de divisão que ocupa toda a tela. Usando Controladores de Navegação e Pilhas Cada controlador de navegação possui um controlador de vista de raiz. Este controlador forma a base da sua pilha. Você pode programaticamente empurrar outros controladores para a pilha como o usuário faz escolhas enquanto navega através da árvore modelrsquos. Embora a árvore em si pode ser multidimensional, o userrsquos caminho (essencialmente a sua história) é sempre uma linha reta representando as escolhas já feitas até à data. Mover para uma nova opção estende a trilha de navegação e cria automaticamente um botão Voltar sempre que um novo controlador de exibição é empurrado para a pilha. Os usuários podem tocar em um botão Voltar para excluir os controladores da pilha. O nome de cada botão é o título do controlador de exibição mais recente. À medida que você retorna através da pilha de controladores de exibição anteriores, cada botão Voltar indica o controlador de exibição que pode ser retornado. Os usuários podem pop back até chegar à raiz. Então eles não podem ir mais longe. A raiz é a base da árvore, e você não pode pop além dessa raiz. Esse design baseado em pilha permanece mesmo quando você planeja usar apenas um controlador de exibição. Você pode usar 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 a raiz via initWithRootViewController :. Empurrando e Exibindo os Controladores de Vista Adicione novos itens na pilha de navegação empurrando um novo controlador com pushViewController: animated. Cada controlador de visualização fornece uma propriedade navigationController. Esta propriedade aponta para o controlador de navegação em que este controlador está participando. A propriedade isnil se o controlador não for empurrado para uma pilha de navegação. Use a propriedade navigationController para empurrar um novo controlador de exibiçã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 da direita (supondo que você definir animado para SIM). Aparece um botão Voltar para a esquerda, indicando um passo atrás na pilha. O botão Voltar usa um chevron juntamente com o título do controlador de visualização anterior na pilha de navegação. Substitua o chevron 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 da Apple Human Interface Guidelines (HIG). Você pode empurrar uma nova visão por muitas razões. Normalmente, isso envolve navegar para visualizações especializadas, como exibições de detalhes ou perfurar 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 toca um botão, um item de tabela ou um acessório de divulgação. Therersquos pouco razão para sempre subclasse UINavigationController. Execute solicitações 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 a partir dos controladores filho. Para a maior parte, você não precisa acessar o controlador de navegação diretamente. As exceções a essa regra incluem o gerenciamento dos botões barrsquos de navegação, a alteração do look barrsquos e a inicialização com uma classe de barra de navegação personalizada. Você pode alterar um estilo de barra ou sua cor de matiz acessando diretamente a propriedade thenavigationBar, 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 é redefinida para tintar itens de botão de barra. Para adicionar novos botões, você modifica o seu navigationItem, que fornece uma classe representacional que descreve o conteúdo mostrado na barra de navegação, incluindo seus itens de botão de barra esquerda e direita e sua exibiçã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: auto action: selector (performAction :) Para remover um botão, atribua o item a nil. Os itens do 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 barras de ferramentas usam para construir botões reais nas interfaces. O iOS não fornece acesso às exibições de botão criadas pelos itens de botão de barra e seus itens de navegação. A partir do iOS 5, você pode adicionar itens de botão de barra múltipla à esquerda e à direita. Atribua uma matriz para as propriedades rightBarButtonItems (observe o s) ou leftBarButtonItems para o item de navegação: O foco de design do iOS 7 é seu applicationrsquos contentmdashmore especificamente, seu conteúdo userrsquos. Bordas e sombras foram removidas e transparência foi adicionada em barras de navegação e outros elementos da interface do usuário. Esta alteração afeta significativamente o layout de suas visualizações, especialmente quando o seu uso usando uma barra de navegação. Começando com o iOS 7, todos os controladores de exibição usam o layout de tela inteira. A propriedade wantsFullScreenLayout no UIViewController foi reprovada, e configurá-la como NO provavelmente levará a um layout muito inesperado. Com o layout de tela cheia, o controlador de exibição dimensionará sua exibição para preencher toda a tela, passando completamente sob a barra de status do sistema translúcido. Além disso, por padrão, todas as barras no iOS 7 são agora translúcidas para revelar ainda mais o conteúdo subjacente. O fluxo de conteúdo em barras mudará seu conteúdo de maneiras que são estranhas em versões anteriores. Você deve incluir ativamente a área embaixo da barra de status e suas próprias barras em seu layout. Para fornecer mais controle sobre o posicionamento, o UIViewController agora fornece um número de novas propriedades de layout. Gerencie a visibilidade da barra de status no nível do controlador de exibição, implementando prefersStatusBarHidden em suas subclasses e retornando um booleano apropriado. Muitas das novas propriedades permitem o posicionamento ou dimensionamento de vistas com base nas barras exibidas atualmente. Para controladores de exibição, agora você pode especificar quais bordas da exibição devem ser estendidas em barras translúcidas, definindo edgesForFieldedLayout. Por padrão, esta propriedade é UIRectEdgeAllmdashwhich significa que todas as bordas se estenderão através dos elementos translúcidos como mostrado na Figura 7-2 (à esquerda). Você também pode especificar qualquer borda (s) específica (s) ou UIRectEdgeNone, interrompendo a borda de exibição de conteúdo quando ela chega à barra, como mostrado na Figura 7-2 (direita). Por padrão, edgesForFextendedLayout também inclui barras opacas. SetextendedLayoutIncludesOpaqueBars como NO para alterar este comportamento. Figura 7-2 No iOS 7, edgesForcextendedLayout no UIViewController controla a aresta da vista usada para o layout. UIRectEdgeAll, o padrão, estende a borda através das barras translúcidas (esquerda). UIRectEdgeNone pára a borda 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 guia). Por padrão, UIScrollViews automaticamente ajustar suas inserções de conteúdo para lidar com esses elementos barra. Para desativar esse comportamento e gerenciar manualmente as inserções de exibição de rolagem, defina automaticAdjustsScrollViewInsets como NO. Finalmente, para auxiliar na criação de conteúdo nas suas visualizações, o iOS 7 fornece as propriedades topLayoutGuide e bottomLayoutGuide. Essas propriedades indicam as bordas superior e inferior da barra na visualização do controlador de exibição. O local representado 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 barra de guia visiblemdashtop da barra de ferramentas ou barra de guia. Nenhuma barra de ferramentas ou barra de guia visível aparece na tela. Use essas propriedades para criar restrições relativas, posicionando suas subviews em relação às bordas da barra, independentemente da localização do quadro ou da presciência da visibilidade da barra. Use-os com restrições de Layout automático no Interface Builder (IB) ou no código de layout. Fora do Auto Layout, use guias no posicionamento baseado em quadros. Referência o valor de deslocamento na propriedade guidersquos comprimento. Receita: A classe de item de navegação Os objetos que povoam a barra de navegação são colocados em uso usando a classe UINavigationItem, que é uma classe que armazena informações sobre esses objetos. As propriedades do item de navegação incluem os itens do botão de barra esquerda e direita, o título mostrado na barra, a vista usada para mostrar o título e qualquer botão Voltar usado para navegar de volta da vista atual. Esta classe permite anexar botões, texto e outros objetos de UI em três locais-chave: a esquerda, o centro ea direita da barra de navegação. Normalmente, isso funciona para ser um botão regular à direita, algum texto (geralmente o título UIViewControllerrsquos) no meio e um botão Back-style à esquerda. Mas yourquore não se limitou a este layout. Você pode adicionar controles personalizados a qualquer um dos três locais: a esquerda, o centro (área de título) ea direita. Você pode criar barras de navegação com campos de pesquisa no meio, em vez disso, ou controles de segmento, barras de ferramentas, fotos e muito mais. Além disso, você pode adicionar vários itens aos arrays de botão esquerdo e direito. Itrsquos tudo fácil de modificar. Títulos e botões de volta A área de título central é especialmente personalizável. Você pode atribuir um título para o item de navegação como este: self. navigationItem. title Meu Título Isso é equivalente a definir a propriedade de título controllerrsquos view 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 Backrdquo back button backsquos ldquogo. Se você pressionar um novo controlador em cima de um controlador chamado Olá, o botão Voltar indica que ele está vinculado a Olá. Você também pode substituir o título baseado em texto com uma exibição personalizada, como um controle. Este código adiciona um controle segmentado personalizado, mas pode ser uma exibição de imagem, um stepper ou qualquer outra coisa: self. navigationItem. titleView UISegmentedControl alloc initWithItems: itens 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: TÍTULO style: UIBarButtonItemStylePlain target: self action: SELECTOR Você fornece um título e um seletor para chamar. Each call to this macro specifies only the title and selector, tightening up the codersquos readability: self. navigationItem. rightBarButtonItem BARBUTTON(Push, selector(push:)) This version of the macro assumes that the target is self, which is quite common, although you could easily adapt this. The following macro adds a target that you specify: define BARBUTTONTARGET(TITLE, TARGET, SELECTOR) UIBarButtonItem alloc initWithTitle:TITLE style:UIBarButtonItemStylePlain target:TARGET action:SELECTOR The vocabulary of bar buttons you use varies by your particular application demands. Itrsquos easy to create macros for system items provided by Apple, image items created from picture resources, and custom view items, which can embed controls and other non-bar button elements. 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 topLayoutGuide 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 UIModalTransitionStylePartialCurl) // 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:iPadStyleControl 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. navigationItem. 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) withObject: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 UIViewAutoresizingFlexibleHeight 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 window 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 didFinishLaunchingWithOptions:(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 UITabBarController 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 )pageViewController 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 )controllerAtPage:(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 - (void)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) indexOfCurrentViewController ((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 BookLayoutStyleHorizontalScroll) (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 front 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 instance 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 controllers. 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 delegate - (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 source. 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(transform, 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.

No comments:

Post a Comment