Olá pessoal!
Meu nome é Ilya, sou de Tinkoff.ru. Traduzi para você um artigo de Geoff Hackworth sobre como o estilo de apresentação modal mudou no iOS 13, o que afetou e como funciona a compatibilidade com versões anteriores do iOS e do Xcode.

1. Introdução
Até o momento, a WWDC 2019 está chegando ao fim. Como muitos desenvolvedores para iOS, processo lentamente todas as novas informações que a Apple nos deu e, nas próximas semanas (e meses!), Tentarei assistir ao maior número de vídeos possível.
Eu tinha três perguntas sobre meus próprios aplicativos:
- Meus aplicativos atuais funcionam sem problemas no iOS 13? A Apple tem um longo histórico de compatibilidade com versões anteriores, com base na versão do Xcode com a qual o aplicativo foi criado. O histórico mostra que os aplicativos criados no Xcode 10 para iOS 13 se comportarão como se estivessem sendo executados no iOS 12. Mas nem sempre é esse o caso.
- Meus aplicativos funcionam ao criar com o Xcode 11 / iOS 13? A criação usando as ferramentas mais recentes permite que o aplicativo funcione de uma nova maneira, ignorando a compatibilidade com versões anteriores do iOS. Algo está quebrado?
- Quais alterações podem / devem ser feitas para melhorar meus aplicativos ou aproveitar os novos recursos do iOS 13? Essa é a maior tarefa e levará mais tempo para estudar e implementar. Este estudo é para um artigo separado.
Ainda não instalei o iOS 13 em um dispositivo real, mas posso testar o item 1 instalando aplicativos criados no Xcode-10 no simulador do iOS 13.
Ainda estou trabalhando no ponto 2, mas, com base nos meus testes iniciais e na leitura de tweets de outros desenvolvedores que fazem descobertas semelhantes, encontrei várias mudanças comportamentais em meus aplicativos ao criar com o Xcode 11. Tenho muitos vídeos para assistir e informações para assimilação, mas neste post eu quero focar em mudanças imediatamente perceptíveis e potencialmente destrutivas na apresentação do UIViewController no iOS 13.
Alterar o estilo de apresentação modal padrão
Por padrão, uma apresentação modal agora é uma “página” (Folha de Página original), em vez de uma tela inteira. A documentação para modalPresentationStyle
diz:
O padrão é UIModalPresentationAutomatic para iOS, começando com iOS 13.0, e UIModalPresentationFullScreen nas versões anteriores.
Por padrão, o UIViewController, se modalPresentationStyle
definido como modalPresentationStyle, usa o UIModalPresentationPageSheet, mas os controladores do sistema podem usar outros estilos de exibição para o UIModalPresentationAutomatic.
Os efeitos dessa alteração são diferentes para o iPhone e o iPad.
Apresentação modal no iPhone
Os estilos de apresentação da Folha de página, Folha de formulário e Popover no iPhone são adaptados para tela cheia, a menos que o método UIAdaptivePresentationControllerDelegate
seja usado para impedir a adaptação. Por exemplo, a tela de configurações pode ser apresentada no estilo da Folha de formulário, para que seja exibida no modo de tela inteira no iPhone e de forma reduzida no iPad. Tecnicamente, a aparência / adaptação depende da largura. As apresentações Folha de página / Folha de formulário / Popover nos dispositivos Landscape iPhone Plus e XS Max não cobrem a tela inteira porque têm a largura normal. A aparência do iPad depende do tamanho do slide e do modo multitarefa.
As capturas de tela a seguir mostram a apresentação da Folha de formulário no iPhone XS para três casos: compilação no Xcode 10 para iOS 12, compilação no Xcode 10 para iOS 13, compilação no Xcode 11 para iOS 13.

A compatibilidade com versões anteriores do iOS 12 com o iOS 13 para a criação do Xcode 10 resulta em uma exibição em tela cheia. O estilo do UITableView agrupado mudou no iOS 13 para ocultar o espaço acima da primeira seção na ausência de um título. Até a versão do Xcode 10 / iOS 12 se comporta de maneira diferente quando lançada no iOS 13, o que não era o que eu esperava.
A maior mudança no iOS 13, é claro, é a exibição do cartão (original. Aparência semelhante ao cartão). O UIViewController foi reduzido em tamanho e sua parte superior ainda é um pouco visível por trás do recém-introduzido UIViewController. A UIWindow atrás do UIViewController raiz também é um pouco visível. O plano de fundo preto do UIWindow fica ótimo por padrão, especialmente em dispositivos com um entalhe. Alguns dos meus aplicativos definem o plano de fundo do UIWindow para branco (por motivos que não me lembro mais) e parecia muito feio. Eu rapidamente consertei!
Comportamento de UIViewController no novo estilo de exibição modal
Se o UIViewController apresentado mostrar outro UIViewController, os cartões se sobrepõem a uma boa animação. Observe que apenas o último UIViewController mostrado e um pouco do anterior são visíveis:

Outra diferença potencialmente importante no comportamento é o que acontece com o UIViewController de apresentação (apresentação original). Uma apresentação em tela cheia (apresentação em tela cheia) que cubra completamente o UIViewController removerá o UIViewController da hierarquia. Porém, no caso da nova apresentação do cartão, o UIViewController deve permanecer na hierarquia porque ainda está visível. No entanto, embora o usuário possa ver apenas dois UIViewController por vez, a exibição repetida do UIViewController não remove o UIViewController inferior da hierarquia.
Redimensionando
A nova aparência no estilo de cartão significa que o UIViewController mostrado não é tão alto no iOS 13 quanto no iOS 12:

Eu quero uma tela cheia!
Uma solicitação explícita para exibir o UIViewController no modo de tela cheia impedirá a exibição da tela no estilo do cartão. No entanto, isso pode atrapalhar o comportamento do aplicativo no iPad. Não fique tentado a verificar o idioma do dispositivo e use um estilo de apresentação diferente para iPhone e iPad. Se os últimos anos nos ensinaram algo, é que não devemos fazer suposições com base nos tipos de dispositivos ou nos tamanhos de tela. Se você quiser que o iPad exiba Página / Folha de formulário, mas o iPhone tenha Tela cheia, use o UIAdaptivePresentationControllerDelegate
para se adaptar ao modo de tela cheia em condições de largura compacta.
Apresentação modal no iPad
As telas mostradas no estilo Folha de formulário permanecem inalteradas no iOS 13:

Folhas de Páginas
Conforme observado acima, o modalPresentationStyle
padrão no iOS 13 agora é uma Folha de página. No iPad, o tamanho do UIViewController nesse estilo mudou na orientação retrato e paisagem:


Como no iOS 12, a restrição de "conteúdo legível" (tamanho do conteúdo legível original) muda de tamanho ao alterar a categoria de tamanho do conteúdo (categoria de tamanho do conteúdo original). O tamanho real parece diferente no iOS 12 e 13 em algumas categorias de tamanho de conteúdo.
O próprio UIViewController, apresentado no estilo da página da página, também aumenta no iOS 13 com um aumento na categoria de tamanho do conteúdo. Aqui está a categoria "Extra Extra Extra Large" (o tamanho máximo disponível sem incluir tamanhos maiores de acessibilidade):


Outros tipos de apresentação
A documentação para modalPresentationStyle
diz:
Por padrão, o UIViewController define UIModalPresentationAutomatic como UIModalPresentationPageSheet, mas outros controladores de sistema podem definir o UIModalPresentationAutomatic de maneira diferente.
Não tenho 100% de certeza de todas as regras para "o restante dos controladores do sistema", mas descobri que exibir um modalPresentationStyle
tela dividida sem definir modalPresentationStyle
oferece uma visão geral do cartão:

Deslize para dispensar
Outra mudança importante que afeta o iPhone e o iPad é que as telas apresentadas modalmente que não estão no modo de tela cheia (exceto pop-ups) podem ser interativamente fechadas deslizando para baixo. No momento, a tela atrás volta ao modo de tela cheia:

Observe que, neste exemplo, coloquei a tela "Sobre" no UINavigationController da tela de configurações. Embora o UINavigationController não exibisse seu controlador raiz, o fechamento interativo foi possível.
Não me mexa, por favor!
Se você confiar no usuário para clicar no botão Concluir (ou em um botão semelhante) ou voltar para o topo da pilha do controlador de navegação para fechar o UIViewController exibido de forma modal, o novo comportamento de furto a fechar poderá atrapalhar o aplicativo, pois o manipulador de fechamento de tela não será executado.
Por exemplo, no meu aplicativo Pomodoro Timer Pommie, um usuário pode ir para uma sub-tela na tela Configurações e adicionar ou editar um perfil de timer (configuração para períodos de trabalho / intervalo para um tipo específico de tarefa):

No caso de Pommie, acho normal (e seguro) se o usuário fechar toda a tela de configurações com um furto. Os usuários provavelmente esperam poder fechar a tela de uma só vez e quero que meus aplicativos funcionem corretamente no iOS 13. No entanto, sinto que, na tela "Adicionar / alterar perfil do temporizador", não é possível permitir que o usuário feche a tela passando com o dedo, pois existe o risco de perda de mudança. Pode não estar totalmente claro para o usuário o que acontecerá após esse fechamento.
Uma parte da correção desse problema é a nova isModalInPresentation
UIViewController: isModalInPresentation
. A partir da documentação:
modalInPresentation é definido quando você deseja tornar a tela modal. Quando essa opção está ativada, a apresentação impedirá o fechamento interativo e ignorará os eventos fora dos limites do UIViewController até que este parâmetro seja definido como NÃO.
Para obter o comportamento semelhante ao iOS 12 para a minha tela Configurações no iOS 13, eu poderia simplesmente definir true para a propriedade isModalInPresentation
do isModalInPresentation
mostrado normalmente. Se o usuário tentar deslizar para baixo para fechá-lo, a tela mudará um pouco, mas resistirá às ações do usuário e não será fechada.
A propriedade pode ser alterada a qualquer momento, para que você possa, por exemplo, permitir o fechamento se o usuário ainda não tiver feito alterações que serão perdidas se ele não as tiver salvo explicitamente. Porém, depois que a alteração for feita, você poderá definir isModalInPresentation
para impedir o fechamento passando o dedo. Isso forçará o usuário a clicar no botão "Cancelar" ou "Salvar".
Fechar detecção
Conforme observado anteriormente, alguns aplicativos podem precisar executar algum código quando o UIViewController modificável é fechado usando os botões Cancelar, Concluir ou Salvar (além de apenas fechá-lo). Por exemplo, pode ser necessário reiniciar o cronômetro no jogo ou agir com base em algumas informações que o usuário alterou. Este código não será executado se o usuário fechar a tela passando o dedo. Seu botão não é pressionado, portanto, seu manipulador de ações não será chamado. Isso pode atrapalhar o comportamento do seu aplicativo.
A maneira mais fácil de evitar esse problema é impedir o fechamento interativo usando isModalInPresentation. O usuário terá que pressionar um botão para fechar o controlador de exibição, exatamente como era antes do iOS 13. Mas há outra maneira ...
O iOS 13 adiciona alguns novos métodos UIAdaptivePresentationControllerDelegate. Eles permitem que outro objeto (geralmente uma tela que mostra outra tela ser modal) controle se o fechamento interativo é permitido (uma alternativa ao uso de isModalInPresentation
) e receba informações sobre quando um fechamento interativo é iniciado ou encerrado. Esses métodos estão bem documentados e explicados claramente na WWDC 2019 224: Modernizando sua interface do usuário para o IOS 13 , a partir do 15º minuto. Observe que o presentationControllerWillDismiss
pode ser chamado várias vezes se o usuário começar a deslizar para fechar, mudar de idéia e deslizar novamente. No método presentationControllerDidDismiss
, é necessário executar um código adicional chamado quando você clica no botão Cancelar, Concluir ou Salvar (é claro, não é necessário fechar a tela mostrada). Esses métodos não serão chamados se o UIViewController estiver fechado programaticamente. Portanto, você ainda precisará executar seu código no manipulador de botões (ou em seu próprio delegado), o que causa o fechamento mesmo ao trabalhar no iOS 13.
Vejamos o método delegado presentationControllerDidAttemptToDismiss
. Ele será chamado se o usuário tentar deslizar para fechar, mas isModalInPresentation
resultou em um bloqueio de bloqueio. No vídeo com WWDC, propõe-se mostrar uma lista de ações com a pergunta se o usuário deseja descartar as alterações ou salvá-las. Parece uma boa idéia se o UIViewController mostrado tiver os botões Cancelar e Salvar / Concluído: crie uma nova anotação, edite as propriedades do objeto etc.
Eu acho que para um UIViewController aninhado na pilha de navegação com os botões Cancelar e Salvar, isso é mais complicado. O código para executar o salvamento provavelmente está no UIViewController, que é um nível mais alto na pilha, e não no objeto que implementa o UIAdaptivePresentationControllerDelegate
. Tentar redirecionar a seleção de um usuário para um objeto que pode executar o salvamento pode não ser totalmente apropriado. Em meus próprios aplicativos, acho que vou bloquear o fechamento de telas que exigem uma ação explícita de desfazer / salvar se elas não estiverem no topo da pilha de navegação.
Recursos
O vídeo da WWDC 2019 será o melhor lugar para descobrir o que mudou no iOS 13, quais alterações você precisa fazer em seus aplicativos para que funcionem corretamente ao criar no Xcode 11 e quais alterações você pode fazer para melhorá-los e aproveitar as novas vantagens. funções Aqui estão alguns vídeos para você começar:
Conclusão
Até agora, não encontrei nenhum problema com meus aplicativos criados no Xcode 10 no iOS 13. A compatibilidade com versões anteriores realmente funciona aqui. Fiquei um pouco surpreso ao ver uma mudança na aparência de uma mesa agrupada.
As compilações do Xcode 11 precisavam de algumas pequenas correções para lidar com as alterações nas representações modais discutidas neste post. Provavelmente haverá mudanças que ainda não descobri.
Teste suas apresentações modais completamente (especialmente com barras de pesquisa)! Decida se você deseja permitir que o usuário feche as telas modais com isModalInPresentation
e use isModalInPresentation
para obter o comportamento necessário para evitar a perda acidental de dados devido a deslizamentos errados. Para maior flexibilidade e controle, use o UIAdaptivePresentationControllerDelegate
.