Digamos que eu tenha vários controladores de visualização em meu aplicativo Swift e quero poder passar dados entre eles. Se estou vários níveis abaixo em uma pilha de controlador de exibição, como faço para passar dados para outro controlador de exibição? Ou entre as guias em um controlador de visualização da barra de guias?
(Observe, essa pergunta é um "toque".) Ela é tão questionada que resolvi escrever um tutorial sobre o assunto. Veja minha resposta abaixo.
Respostas:
Sua pergunta é muito ampla. Sugerir que existe uma solução abrangente para cada cenário é um pouco ingênuo. Então, vamos examinar alguns desses cenários.
O cenário mais comum questionado sobre Stack Overflow na minha experiência é a simples passagem de informações de um controlador de visualização para o próximo.
Se estivermos usando storyboard, nosso primeiro controlador de visualização pode substituir
prepareForSegue
, que é exatamente o que existe. UmUIStoryboardSegue
objeto é passado quando este método é chamado e contém uma referência ao nosso controlador de visualização de destino. Aqui, podemos definir os valores que queremos passar.Alternativamente, se não estivermos usando storyboards, estamos carregando nosso controlador de visualização de uma ponta. Nosso código é um pouco mais simples então.
Em ambos os casos,
myInformation
é uma propriedade em cada controlador de visualização contendo todos os dados que precisam ser passados de um controlador de visualização para o próximo. Eles obviamente não precisam ter o mesmo nome em cada controlador.Também podemos querer compartilhar informações entre as guias em a
UITabBarController
.Nesse caso, é potencialmente ainda mais simples.
Primeiro, vamos criar uma subclasse de
UITabBarController
e dar a ela propriedades para qualquer informação que desejamos compartilhar entre as várias guias:Agora, se estamos construindo nosso aplicativo a partir do storyboard, simplesmente alteramos a classe do controlador da barra de guias do padrão
UITabBarController
paraMyCustomTabController
. Se não estivermos usando um storyboard, simplesmente instanciamos uma instância dessa classe personalizada em vez daUITabBarController
classe padrão e adicionamos nosso controlador de visualização a ela.Agora, todos os nossos controladores de visualização dentro do controlador da barra de guias podem acessar esta propriedade da seguinte forma:
E ao
UINavigationController
criar subclasses da mesma maneira, podemos usar a mesma abordagem para compartilhar dados em uma pilha de navegação inteira:Existem vários outros cenários. De forma alguma esta resposta cobre todos eles.
fonte
prepareForSegue
. É uma pena que esta observação muito simples se perca entre as outras respostas e digressões aqui.prepareForSegue
outras transferências diretas de informações em quase todos os cenários e então simplesmente concordar com os novatos quando eles aparecem com o cenário para o qual essas situações não funcionam e, então, temos que ensiná-los sobre essas abordagens mais globais.Essa pergunta surge o tempo todo.
Uma sugestão é criar um singleton de contêiner de dados: Um objeto que é criado uma vez e apenas uma vez na vida de seu aplicativo e persiste por toda a vida de seu aplicativo.
Essa abordagem é adequada para uma situação em que você tem dados globais de aplicativo que precisam estar disponíveis / modificáveis em diferentes classes em seu aplicativo.
Outras abordagens, como a configuração de links unilaterais ou bidirecionais entre controladores de visualização, são mais adequadas para situações em que você está passando informações / mensagens diretamente entre controladores de visualização.
(Veja a resposta de nhgrif, abaixo, para outras alternativas.)
Com um singleton de contêiner de dados, você adiciona uma propriedade à sua classe que armazena uma referência ao seu singleton e, em seguida, usa essa propriedade sempre que precisar de acesso.
Você pode configurar seu singleton para que ele salve seu conteúdo em disco para que o estado do seu aplicativo persista entre as inicializações.
Eu criei um projeto de demonstração no GitHub demonstrando como você pode fazer isso. Aqui está o link:
Projeto SwiftDataContainerSingleton no GitHub Aqui está o README desse projeto:
SwiftDataContainerSingleton
Uma demonstração do uso de um singleton de contêiner de dados para salvar o estado do aplicativo e compartilhá-lo entre objetos.
A
DataContainerSingleton
classe é o único singleton real.Ele usa uma constante estática
sharedDataContainer
para salvar uma referência ao singleton.Para acessar o singleton, use a sintaxe
O projeto de amostra define 3 propriedades no contêiner de dados:
Para carregar a
someInt
propriedade do contêiner de dados, você usaria um código como este:Para salvar um valor em someInt, você usaria a sintaxe:
O
init
método DataContainerSingleton adiciona um observador para oUIApplicationDidEnterBackgroundNotification
. Esse código é parecido com este:No código do observador, ele salva as propriedades do contêiner de dados em
NSUserDefaults
. Você também pode usarNSCoding
Core Data ou vários outros métodos para salvar dados de estado.O
init
método DataContainerSingleton também tenta carregar valores salvos para suas propriedades.Essa parte do método init se parece com isto:
As chaves para carregar e salvar valores em NSUserDefaults são armazenadas como constantes de string que fazem parte de uma estrutura
DefaultsKeys
, definidas assim:Você faz referência a uma dessas constantes assim:
Usando o singleton do contêiner de dados:
Este aplicativo de amostra faz uso triplo do singleton do contêiner de dados.
Existem dois controladores de visualização. A primeira é uma subclasse personalizada de UIViewController
ViewController
e a segunda é uma subclasse personalizada de UIViewControllerSecondVC
.Ambos os controladores de visão têm um campo de texto neles, e ambos carregam um valor da
someInt
propriedade de singlelton do contêiner de dados no campo de texto em seuviewWillAppear
método, e ambos salvam o valor atual do campo de texto de volta em `someInt 'do contêiner de dados.O código para carregar o valor no campo de texto está no
viewWillAppear:
método:O código para salvar o valor editado pelo usuário de volta no recipiente de dados está nos
textFieldShouldEndEditing
métodos dos controladores de visualização :Você deve carregar valores em sua interface com o usuário em viewWillAppear em vez de viewDidLoad para que sua IU seja atualizada cada vez que o controlador de visualização for exibido.
fonte
Swift 4
Existem muitas abordagens para a passagem rápida de dados. Aqui estou adicionando algumas das melhores abordagens disso.
1) Usando o StoryBoard Segue
As sequências de storyboard são muito úteis para passar dados entre os controladores de visualização de origem e destino e vice-versa.
2) Usando métodos de delegação
ViewControllerD
ViewControllerC
fonte
ViewControllerA
paraViewControllerB
. Eu apenas coloquei o trecho de código na parte inferior do meuViewControllerA.swift
(ondeViewControllerA.swift
está o nome do seu arquivo, é claro) logo antes da última chave. "prepare
" é na verdade uma função pré-existente embutida especial em uma determinada classe [que não faz nada], e é por isso que você deve "override
" issoOutra alternativa é usar a central de notificações (NSNotificationCenter) e postar notificações. Esse é um acoplamento muito fraco. O remetente de uma notificação não precisa saber ou se importar com quem está ouvindo. Ele apenas posta uma notificação e se esquece dela.
As notificações são boas para a passagem de mensagens um para muitos, uma vez que pode haver um número arbitrário de observadores escutando uma determinada mensagem.
fonte
Em vez de criar um único controlador de dados, eu sugeriria criar uma instância do controlador de dados e distribuí-la. Para oferecer suporte à injeção de dependência, primeiro criaria um
DataController
protocolo:Então, eu criaria uma
SpecificDataController
classe (ou qualquer nome que seja apropriado no momento):A
ViewController
classe deve ter um campo para conter odataController
. Observe que o tipo dedataController
é o protocoloDataController
. Dessa forma, é fácil alternar as implementações do controlador de dados:Em
AppDelegate
podemos definir o viewControllerdataController
:Quando mudamos para um viewController diferente, podemos passar o seguinte
dataController
:Agora, quando desejamos trocar o controlador de dados para uma tarefa diferente, podemos fazer isso no
AppDelegate
e não precisamos alterar nenhum outro código que use o controlador de dados.É claro que isso é um exagero se quisermos simplesmente passar um único valor. Nesse caso, é melhor seguir a resposta de nhgrif.
Com essa abordagem, podemos separar a visualização da parte lógica.
fonte
Como @nhgrif apontou em sua excelente resposta, há muitas maneiras diferentes pelas quais os VCs (controladores de visualização) e outros objetos podem se comunicar uns com os outros.
O singleton de dados que delineei em minha primeira resposta é realmente mais sobre como compartilhar e salvar o estado global do que comunicar-se diretamente.
A resposta do nhrif permite enviar informações diretamente da fonte para o VC de destino. Como mencionei na resposta, também é possível enviar mensagens de volta do destino para a origem.
Na verdade, você pode configurar um canal ativo unilateral ou bidirecional entre diferentes controladores de visualização. Se os controladores de visualização estiverem vinculados por meio de uma sequência de storyboard, a hora de configurar os links está no método prepareFor Segue.
Eu tenho um projeto de amostra no Github que usa um controlador de visualização pai para hospedar 2 visualizações de tabela diferentes como crianças. Os controladores de visualização filho são vinculados usando segues incorporados, e o controlador de visualização pai conecta links de 2 vias com cada controlador de visualização no método prepareForSegue.
Você pode encontrar esse projeto no github (link). Eu o escrevi em Objective-C, no entanto, e não o converti para Swift, então se você não estiver confortável em Objective-C, pode ser um pouco difícil de seguir
fonte
SWIFT 3:
Se você tiver um storyboard com segues identificados, use:
Embora se você fizer tudo programaticamente, incluindo a navegação entre diferentes UIViewControllers, use o método:
Nota: para usar a segunda maneira que você precisa para tornar seu UINavigationController, você está enviando UIViewControllers, um delegado e ele precisa estar em conformidade com o protocolo UINavigationControllerDelegate:
fonte
Depende de quando você deseja obter dados.
Se você quiser obter dados sempre que quiser, pode usar um padrão singleton. A classe padrão está ativa durante o tempo de execução do aplicativo. Aqui está um exemplo do padrão singleton.
Se você deseja obter dados após qualquer ação, pode usar o NotificationCenter.
fonte