Como adicionar uma subvisualização que tenha seu próprio UIViewController em Objective-C?

Eu estou lutando com subvisualizações que têm seus próprios UIViewControllers . Eu tenho um UIViewController com uma visão (rosa claro) e dois botões em uma toolbar . Quero que a visualização azul seja exibida quando o primeiro botão for pressionado e a visualização amarela a ser exibida com o segundo botão for pressionada. Deveria ser fácil se eu quisesse exibir uma visão. Mas a visão azul conterá uma tabela, então ela precisa de seu próprio controlador. Essa foi a minha primeira lição. Comecei com essa pergunta SO, onde soube que precisava de um controlador para a mesa.

Então, eu vou voltar e dar alguns passos aqui. Abaixo está uma imagem de um ponto de partida simples com o meu ViewController Utility (o controlador de visualização principal) e os outros dois controladores (azul e amarelo). Imagine que quando o utilitário ViewController (a visualização principal) for exibido pela primeira vez, a visualização azul (padrão) será exibida onde a visualização rosa está localizada. Os usuários poderão clicar nos dois botões para ir e voltar e a visualização rosa NUNCA será exibida. Eu só quero a visão azul para ir onde a visão rosa é e a visão amarela para ir onde a visão rosa é. Espero que isto faça sentido.

Imagem simples de storyboard

Eu estou tentando usar addChildViewController . Pelo que tenho visto, existem duas maneiras de fazer isso: A Visualização de Contêiner no storyboard ou addChildViewController programaticamente. Eu quero fazer isso de forma programática. Eu não quero usar um NavigationController ou uma barra de abas. Eu só quero adicionar os controladores e empurrar a visão correta para a visão rosa quando o botão associado é pressionado.

Abaixo está o código que tenho até agora. Tudo o que quero fazer é exibir a vista azul, onde está a vista rosa. Pelo que eu vi, eu deveria ser capaz de adicionar apenas oChildViewController e addSubView. Este código não está fazendo isso por mim. Minha confusão está me levando a melhor. Alguém pode me ajudar a obter a visão azul exibida onde está a vista rosa?

Este código não tem a intenção de fazer nada além de exibir a visualização azul em viewDidLoad.

IDUtilityViewController.h

 #import  @interface IDUtilityViewController : UIViewController @property (strong, nonatomic) IBOutlet UIView *utilityView; @end 

IDUtilityViewController.m

 #import "IDUtilityViewController.h" #import "IDAboutViewController.h" @interface IDUtilityViewController () @property (nonatomic, strong) IDAboutViewController *aboutVC; @end @implementation IDUtilityViewController - (void)viewDidLoad { [super viewDidLoad]; self.aboutVC = [[IDAboutViewController alloc]initWithNibName:@"AboutVC" bundle:nil]; [self addChildViewController:self.aboutVC]; [self.aboutVC didMoveToParentViewController:self]; [self.utilityView addSubview:self.aboutVC.aboutView]; } @end 

————————–EDITAR———————– ——-

O self.aboutVC.aboutView é nulo. Mas eu liguei no storyboard . Ainda preciso instanciá-lo?

insira a descrição da imagem aqui

Este post, que data dos primórdios do iOS moderno, geralmente tem a syntax mais recente, como o Swift 4 atualmente. Se você está começando com o iOS, autolayout, etc, você vai começar.

No iOS de hoje “tudo é uma exibição de contêiner” . É a maneira básica de fazer aplicativos hoje.

Um aplicativo pode ser tão simples que tenha apenas uma visualização. Mas mesmo nesse caso, cada “coisa” na canvas é uma exibição de contêiner.

É tão fácil …


(A) Arraste uma vista de contêiner para a sua cena …

Arraste uma vista de contêiner para a sua vista de cena. (Assim como você iria arrastar, digamos, um UIButton.)

A vista do contêiner é a coisa marrom nesta imagem. Na verdade, está dentro da sua visão de cena.

insira a descrição da imagem aqui

Quando você arrasta uma vista de contêiner para a sua vista de cena, o Xcode automaticamente lhe dá duas coisas :

  1. Você obtém a visualização de contêiner dentro da visualização de cena e,

  2. você ganha um novíssimo UIViewController que fica em algum lugar no branco do seu storyboard .

Os dois estão conectados com o “Símbolo Maçônico” – explicado abaixo!


(B) Clique no novo controlador de visão (a nova coisa que o Xcode fez para você em algum lugar na área branca, não a coisa dentro da sua cena ) … e, mude a class!

É tão simples assim.

Você está feito.


Aqui está a mesma coisa explicada visualmente.

Observe a exibição do contêiner em (A) .

Observe o controlador em (B) .

mostra uma visualização de contêiner e o controlador de exibição associado

Clique em B. (Isso é B – não A!)

Vá para o inspetor no canto superior direito. Observe que diz “UIViewController”

[ insira a descrição da imagem aqui ] [3]

Altere-o para sua própria class customizada, que é um UIViewController.

insira a descrição da imagem aqui

Então, eu tenho uma class Swift Snap que é um UIViewController .

insira a descrição da imagem aqui

Então, onde diz “UIViewController” no Inspetor eu digitei “Snap”.

(Como de costume, o Xcode completará automaticamente o “Snap” quando você começar a digitar “Snap …”.)

Isso é tudo que existe para isso – você está feito.


Como alterar a exibição do contêiner – digamos, para uma exibição de tabela.

Portanto, quando você clica para adicionar uma visualização de contêiner, a Apple fornece automaticamente um controlador de visualização vinculado, sentado no storyboard.

Por acaso (2017): torna-se um UIViewController por padrão.

Isso é bobagem: deve perguntar de que tipo você precisa. Por exemplo, muitas vezes você precisa de uma exibição de tabela. Veja como alterá-lo para algo diferente:

No momento da escrita, o Xcode fornece um UIViewController por padrão. Digamos que você queira um UICollectionViewController :

(i) Arraste uma exibição de contêiner para sua cena. Olhe para o UIViewController no storyboard que o Xcode lhe dá por padrão.

(ii) Arraste um novo UICollectionViewController para qualquer lugar na área branca principal do storyboard.

(iii) Clique na visualização de contêiner dentro de sua cena. Clique no inspetor de conexões. Observe que há um “Triggered Segue”. Passe o mouse sobre o “Triggered Segue” e observe que o Xcode realça todo o UIViewController indesejado.

(iv) Clique no “x” para realmente excluir a Segmentação Disparada.

(v) Arraste a partir daquela Segmentação Disparada (viewDidLoad é a única opção). Arraste o storyboard para o novo UICollectionViewController. Deixe ir e um pop-up aparece. Você deve selecionar incorporar .

(vi) Simplesmente apague todo o UIViewController indesejado. Você está feito.

Versão curta: exclua o UIViewController indesejado. Coloque um novo UICollectionViewController no storyboard. Controlar e arrastar de: Conexões da vista de contêiner – Trigger Segue – viewDidLoad, para, seu novo controlador. Certifique-se de selecionar “incorporar” no pop-up.


Inserindo o identificador de texto …

Você terá uma dessas coisas de símbolo maçônico “quadrado em um quadrado” : está na “linha flexível” que conecta a sua vista de contêiner com o controlador de visão.

A coisa “símbolo maçônico” é a segue.

insira a descrição da imagem aqui

Selecione o segue clicando no item “símbolo maçônico”.

Olhe para a direita.

Você DEVE digitar um identificador de texto para o segue.

Você decide o nome. Pode ser qualquer string de texto. Uma escolha sensata é frequentemente “segueClassName”.

Se você seguir esse padrão, todos os seus segues serão chamados deixaClockView, seguePersonSelector, segueSnap, segueCards e assim por diante.

Em seguida, onde você usa esse identificador de texto?


Como conectar-se ‘ao’ controlador infantil …

Em seguida, faça o seguinte, em código, no ViewController de toda a cena.

Digamos que você tenha três visualizações de contêiner na cena. Cada exibição de contêiner contém um controlador diferente, digamos “Snap”, “Clock” e “Other”.

A mais recente syntax do Swift3 (2017)

 var snap:Snap? var clock:Clock? var other:Other? override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if (segue.identifier == "segueSnap") { snap = (segue.destination as! Snap) } if (segue.identifier == "segueClock") { clock = (segue.destination as! Clock) } if (segue.identifier == "segueOther") { other = (segue.destination as! Other) } } 

É simples assim. Você conecta uma variável para se referir aos controladores, usando a chamada prepareForSegue .


Como se conectar na ‘outra direção’, até o pai …

Digamos que você esteja “no” controlador que você colocou em uma visualização de contêiner (“Snap” no exemplo).

Pode ser confuso chegar ao controlador de visão “chefe” acima de você (“Traço” no exemplo). Felizmente, é assim tão simples:

 // Dash is the overall scene. // Here we are in Snap. Snap is one of the container views inside Dash. class Snap { var myBoss:Dash? override func viewDidAppear(_ animated: Bool) { // MUST be viewDidAppear super.viewDidAppear(animated) myBoss = self.parent as? Dash } 

Crítico: funciona apenas a partir de viewDidAppear ou posterior. Não funcionará no viewDidLoad .

Você está feito.


Importante: isso funciona apenas para visualizações de contêiner.

Dica avançada importante: não se esqueça de que isso funciona apenas para visualizações de contêiner.

Hoje em dia, com identificadores de storyboard, é comum apenas aparecer novas visualizações na canvas (como no desenvolvimento do Android). Então, digamos que o usuário queira editar alguma coisa …

  // let's just pop a view on the screen. // this has nothing to do with container views // let e = ...instantiateViewController(withIdentifier: "Edit") as! Edit e.modalPresentationStyle = .overCurrentContext self.present(e, animated: false, completion: nil) 

Ao usar uma visualização de contêiner, É GARANTIDO que o Dash seja o controlador de visualização pai do Snap.

No entanto, isso NÃO É NECESSARIAMENTE O CASO quando você usa o instantiateViewController.

Muito confusamente, no iOS o controlador de exibição pai não está relacionado à class que o instanciou. (Pode ser o mesmo, mas geralmente não é o mesmo.) O padrão self.parent é apenas para exibições de contêiner.

(Para um resultado semelhante no padrão instantiateViewController, você deve usar um protocolo e um delegado, lembrando que o delegado será um link fraco.)


prepareForSegue mal nomeado …

Vale a pena notar que “prepareForSegue” é um nome muito ruim!

“prepareForSegue” é usado para duas finalidades: carregar visualizações de contêiner e seguir entre cenas.

Mas na prática, você raramente segue entre cenas! Considerando que quase todos os aplicativos têm muitos, muitos, visualizações de contêineres, é claro.

Faria muito mais sentido se “prepareForSegue” fosse chamado algo como “loadingContainerView”.


Mais de um…

Uma situação comum é: você tem uma pequena área na canvas, onde deseja mostrar um dos vários controladores de visualização diferentes. Por exemplo, um dos quatro widgets.

A maneira mais simples de fazer isso: basta ter quatro visualizações de contêiner diferentes, todas sentadas na mesma área idêntica . Em seu código, simplesmente oculte todos os quatro e ative o que você quer que seja visível.

No storyboard, tenha um UIView “titular” vazio, que simplesmente mantenha as quatro visualizações de contêiner. Você pode então dimensionar ou mover todos os quatro de uma só vez dimensionando ou movendo o “suporte”. Em seu código, basta ter quatro tomadas UIView , uma para cada uma das visualizações de contêiner. Copie e cole o código acima, “Como conectar-se ao controlador filho”, para conectar os quatro controladores de visualização contidos.


Nota – Referências Storyboard chegar!

Como SimplGy aponta abaixo, “As Referências do Storyboard do iOS 9 tornam as visualizações de contêiner ainda mais impressionantes. Você pode definir sua visualização reutilizável onde quiser e fazer referência a ela a partir de qualquer exibição de contêiner em vários storyboards modulares”.

Note também que – bastante confuso – muitas vezes hoje você simplesmente não se incomoda com visualizações de contêineres!

Você simplesmente instantiateViewController#withIdentifier em muitas situações.

Mas observe o “gotchya” sobre o .parent explicado acima. O ponto principal das visualizações de contêiner é que você é instantaneamente e simplesmente assegurado da cadeia pai.

Se você usar o instantiateViewController#withIdentifier usando uma referência de storyboard, será necessário mexer em um protocolo e um delegado (lembrando que o delegado será um link fraco). Mas então você pode usá-lo “on the fly” de forma flexível em qualquer lugar.

Em contraste, usar uma visão de contêiner “fixa”, por assim dizer, é extremamente simples, e você conecta-se instantaneamente entre pai e filho, como explicado acima.

Eu vejo dois problemas. Primeiro, desde que você esteja criando os controladores no storyboard, você deve instanciá-los com instantiateViewControllerWithIdentifier: initWithNibName:bundle: não initWithNibName:bundle: Em segundo lugar, quando você adiciona a visão como uma subvisualização, você deve dar a ela um quadro. Assim,

 - (void)viewDidLoad { [super viewDidLoad]; self.aboutVC = [self.storyboard instantiateViewControllerWithIdentifier:@"aboutVC"]; // make sure you give the controller this same identifier in the storyboard [self addChildViewController:self.aboutVC]; [self.aboutVC didMoveToParentViewController:self]; self.aboutVC.view.frame = self.utilityView.bounds; [self.utilityView addSubview:self.aboutVC.aboutView]; }