Eu tenho um ponteiro para a UIView. Como faço para acessar os seus UIViewController? [self superview]é outro UIView, mas não o UIViewController, certo?
Basicamente, estou tentando chamar o viewWillAppear do meu viewController, pois meu modo de exibição está sendo descartado. A exibição está sendo descartada pela própria exibição, que está detectando um toque e chamando [self removeFromSuperview]; O viewController não está chamando o viewWillAppear / WillDisappear / DidAppear / DidDisappear.
mahboudz 03/09/09
Eu quis dizer que estou tentando chamar viewWillDisappear, pois minha exibição está sendo descartada.
Sim, superviewé a visualização que contém sua visualização. Sua visão não deve saber exatamente qual é seu controlador de visão, porque isso violaria os princípios do MVC.
O controlador, por outro lado, sabe por qual visão é responsável por ( self.view = myView) e, geralmente, essa visão delega métodos / eventos para manipulação no controlador.
Normalmente, em vez de um ponteiro para sua visão, você deve ter um ponteiro para seu controlador, que por sua vez pode executar alguma lógica de controle ou passar algo para sua visão.
Não tenho certeza se isso violaria os princípios do MVC. A qualquer momento, uma visualização possui apenas um controlador de visualização. Ser capaz de acessá-lo para transmitir uma mensagem de volta a ele deve ser um recurso automático, não aquele em que você deve trabalhar para alcançar (adicionando uma propriedade para acompanhar). Pode-se dizer o mesmo sobre os pontos de vista: por que você precisa saber quem é seu filho? Ou se há outras visualizações de irmãos. No entanto, existem maneiras de obter esses objetos.
mahboudz
Você está certo sobre a visualização, sabendo sobre seu pai, não é uma decisão de projeto super clara, mas já está estabelecido para executar algumas ações, usando diretamente uma variável de membro de superview (verifique o tipo de pai, remova do pai, etc.). Tendo trabalhado com o PureMVC recentemente, eu me tornei um pouco mais exigente quanto à abstração do design :) Eu faria paralelo entre as classes UIView e UIViewController do iPhone e as classes View e Mediador do PureMVC - na maioria das vezes, a classe View não precisa conheça seu manipulador / interface MVC (UIViewController / Mediador).
Dimitar Dimitrov
9
Palavra-chave: "mais".
Glenn Maynard
278
A partir da UIResponderdocumentação para nextResponder:
A classe UIResponder não armazena ou define o próximo respondedor automaticamente, retornando nulo por padrão. As subclasses devem substituir esse método para definir o próximo respondedor. O UIView implementa esse método retornando o objeto UIViewController que o gerencia (se houver) ou sua superview (se não houver) ; UIViewController implementa o método retornando a superview de sua view; UIWindow retorna o objeto de aplicativo e UIApplication retorna nulo.
Portanto, se você recursar uma visualização nextResponderaté que seja do tipo UIViewController, terá o viewController pai de qualquer visualização.
Observe que ele ainda pode não ter um controlador de exibição pai. Mas somente se a exibição não fizer parte da hierarquia de exibição de um viewController.
Apenas uma coisa: se você se preocupa com a poluição por categoria, apenas defina-a como uma função estática e não como uma macro. Além disso, a conversão brutal é perigosa e, além disso, a macro pode não estar correta, mas não tenho certeza.
Mjuba
A macro funciona, eu só uso a versão macro pessoalmente. Começo muitos projetos e tenho um cabeçalho que simplesmente apareço em todos os lugares com várias dessas funções de utilitário de macro. Economiza tempo. Se você não gosta de macros, pode adaptá-lo a uma função, mas as funções estáticas parecem entediantes, pois você deve colocar uma em cada arquivo que deseja usá-la. Parece que você desejaria uma função não estática declarada em um cabeçalho e definida em um .m em algum lugar?
Mxcl
É bom evitar alguns delegados ou notificações. Obrigado!
Ferran Maylinch
Você deve torná-lo uma extensão de UIResponder;). Post muito edificante.
ScottyBlades
32
resposta @andrey em uma linha (testada no Swift 4.1 ):
parentViewControllernão pode ser definido publicse a extensão estiver no mesmo arquivo que UIViewvocê pode configurá-lo fileprivate, ele será compilado, mas não funcionará! 😐
Está funcionando bem. Eu usei isso para a ação do botão voltar dentro do arquivo .xib do cabeçalho comum.
McDonal_11
23
Apenas para fins de depuração, você pode chamar _viewDelegatevisualizações para obter seus controladores de visualização. Esta é uma API privada, portanto não é segura para a App Store, mas é útil para depuração.
Outros métodos úteis:
_viewControllerForAncestor- obtenha o primeiro controlador que gerencia uma exibição na cadeia de superview. (obrigado n00neimp0rtant)
_rootAncestorViewController - obtém o controlador ancestral cuja hierarquia de visualizações está definida na janela atualmente.
Foi exatamente por isso que cheguei a essa pergunta. Aparentemente, 'nextResponder' faz a mesma coisa, mas eu aprecio o insight que essa resposta fornece. Eu entendo e gosto do MVC, mas a depuração é um animal diferente!
mbm29414
4
Parece que isso funciona apenas na visualização principal do controlador de exibição, e não em nenhuma subvisualização dele. _viewControllerForAncestorpercorrerá as supervistas até encontrar o primeiro que pertence a um controlador de exibição.
precisa saber é o seguinte
Obrigado @ n00neimp0rtant! Estou votando esta resposta para que as pessoas vejam seu comentário.
eyuelt
Atualize a resposta com mais métodos.
Leo Natan
1
Isso é útil ao depurar.
585 ev evinin
10
Para obter referência ao UIViewController com o UIView, você pode fazer a extensão do UIResponder (que é uma super classe para o UIView e o UIViewController), que permite percorrer a cadeia de respostas e, assim, alcançar o UIViewController (caso contrário, não retornará nada).
Eu acho que você pode propagar a torneira para o controlador de exibição e deixá-lo lidar com isso. Essa é uma abordagem mais aceitável. Quanto ao acesso a um controlador de exibição a partir de sua exibição, você deve manter uma referência a um controlador de exibição, pois não há outra maneira. Consulte este tópico, pode ajudar:
Acessando o controlador de exibição de uma visualização
Se você tiver várias visualizações e uma fechar todas as visualizações, e você precisar chamar viewWillDisappear, não seria mais fácil para essa visualização detectar a torneira do que entregá-la ao controlador de visualização e fazer com que o controlador de visualização seja verificado com todas as visualizações para ver em qual delas foi tocada?
Infelizmente, isso é impossível, a menos que você subclasse a view e forneça a ela uma propriedade de instância ou semelhante que armazene a referência do controlador de view dentro dela assim que a view for adicionada à cena ...
Na maioria dos casos - é muito fácil contornar o problema original desta postagem, pois a maioria dos controladores de exibição são entidades bem conhecidas do programador responsável por adicionar quaisquer subvisões ao ViewController's View ;-) É por isso que acho que a Apple nunca se preocupou em adicionar essa propriedade.
Respostas:
Sim,
superview
é a visualização que contém sua visualização. Sua visão não deve saber exatamente qual é seu controlador de visão, porque isso violaria os princípios do MVC.O controlador, por outro lado, sabe por qual visão é responsável por (
self.view = myView
) e, geralmente, essa visão delega métodos / eventos para manipulação no controlador.Normalmente, em vez de um ponteiro para sua visão, você deve ter um ponteiro para seu controlador, que por sua vez pode executar alguma lógica de controle ou passar algo para sua visão.
fonte
A partir da
UIResponder
documentação paranextResponder
:Portanto, se você recursar uma visualização
nextResponder
até que seja do tipoUIViewController
, terá o viewController pai de qualquer visualização.Observe que ele ainda pode não ter um controlador de exibição pai. Mas somente se a exibição não fizer parte da hierarquia de exibição de um viewController.
Extensão Swift 3 e Swift 4.1 :
Extensão Swift 2:
Categoria Objective-C:
Essa macro evita a categoria de poluição:
fonte
UIResponder
;). Post muito edificante.resposta @andrey em uma linha (testada no Swift 4.1 ):
uso:
fonte
parentViewController
não pode ser definidopublic
se a extensão estiver no mesmo arquivo queUIView
você pode configurá-lofileprivate
, ele será compilado, mas não funcionará! 😐Apenas para fins de depuração, você pode chamar
_viewDelegate
visualizações para obter seus controladores de visualização. Esta é uma API privada, portanto não é segura para a App Store, mas é útil para depuração.Outros métodos úteis:
_viewControllerForAncestor
- obtenha o primeiro controlador que gerencia uma exibição na cadeia de superview. (obrigado n00neimp0rtant)_rootAncestorViewController
- obtém o controlador ancestral cuja hierarquia de visualizações está definida na janela atualmente.fonte
_viewControllerForAncestor
percorrerá as supervistas até encontrar o primeiro que pertence a um controlador de exibição.Para obter referência ao UIViewController com o UIView, você pode fazer a extensão do UIResponder (que é uma super classe para o UIView e o UIViewController), que permite percorrer a cadeia de respostas e, assim, alcançar o UIViewController (caso contrário, não retornará nada).
fonte
A maneira rápida e genérica no Swift 3:
fonte
Se você não está familiarizado com o código e deseja encontrar o ViewController corespondendo a determinada exibição, tente:
Na maioria dos casos, você obterá o UIView, mas, de tempos em tempos, haverá uma classe baseada no UIViewController.
fonte
Eu acho que você pode propagar a torneira para o controlador de exibição e deixá-lo lidar com isso. Essa é uma abordagem mais aceitável. Quanto ao acesso a um controlador de exibição a partir de sua exibição, você deve manter uma referência a um controlador de exibição, pois não há outra maneira. Consulte este tópico, pode ajudar: Acessando o controlador de exibição de uma visualização
fonte
Mais código seguro de tipo para Swift 3.0
fonte
Infelizmente, isso é impossível, a menos que você subclasse a view e forneça a ela uma propriedade de instância ou semelhante que armazene a referência do controlador de view dentro dela assim que a view for adicionada à cena ...
Na maioria dos casos - é muito fácil contornar o problema original desta postagem, pois a maioria dos controladores de exibição são entidades bem conhecidas do programador responsável por adicionar quaisquer subvisões ao ViewController's View ;-) É por isso que acho que a Apple nunca se preocupou em adicionar essa propriedade.
fonte
Um pouco tarde, mas aqui está uma extensão que permite encontrar um respondedor de qualquer tipo, incluindo o ViewController.
fonte
Se você definir um ponto de interrupção, poderá colá-lo no depurador para imprimir a hierarquia de exibição:
Você deve encontrar o pai da sua visualização em algum lugar dessa bagunça :)
fonte
recursiveDescription
somente imprime a hierarquia de exibição , não os controladores.