Criando um segue programaticamente

202

Eu tenho um comum UIViewControllerque todos os meusUIViewsControllers esforço para reutilizar algumas operações comuns.

Eu quero configurar um segue sobre este "comum" UIViewControllerpara que todos os outros UIViewControllersherdem.

Estou tentando descobrir como faço isso programaticamente.

Eu acho que a pergunta também pode ser como definir um seguepara todos os meus UIViewControllerssem entrar no storyboard e fazê-los manualmente.

Tiago Veloso
fonte

Respostas:

169

Por definição, um segue não pode realmente existir independentemente de um storyboard. É ainda lá no nome da classe: UIStoryboardSegue. Você não cria segues programaticamente - é o tempo de execução do storyboard que os cria para você. Normalmente, você pode chamar performSegueWithIdentifier:o código do seu controlador de exibição, mas isso depende de ter uma sequência já configurada no storyboard para referência.

O que eu acho que você está perguntando é como você pode criar um método no seu controlador de exibição comum (classe base) que fará a transição para um novo controlador de exibição e será herdado por todas as classes derivadas. Você pode fazer isso criando um método como este no seu controlador de exibição de classe base:

- (IBAction)pushMyNewViewController
{
    MyNewViewController *myNewVC = [[MyNewViewController alloc] init];

    // do any setup you need for myNewVC

    [self presentModalViewController:myNewVC animated:YES];
}

e, em sua classe derivada, chame esse método quando o botão apropriado for clicado ou a linha da tabela for selecionada ou o que for.

jonkroll
fonte
4
Obrigado. É uma pena que não possamos fazê-lo programaticamente. Realmente aumentaria a qualidade do código fonte (menos duplicação é sempre boa). Vou tentar sua sugestão.
Tiago Veloso
2
@ Jonkroll é possível chamar / executar segue da instrução switch, ou seja, com base em qual índice eu tenho?
precisa saber é o seguinte
5
@ codejunkie: Sim, você pode fazer isso. Você usaria o UIViewControllermétodo nomeado performSegueWithIdentifier:sender:para isso.
jonkroll
2
Estou criando e executando segue programaticamente (veja minha resposta). Algo errado com meu código, então, se sua resposta estiver correta?
Jean-Philippe Pellet
13
Atualização para iOS 6+: UIView's presentModalViewController:animated:está obsoleta. Dos documentos - (Preterido no iOS 6.0. Use presentViewController: animated: conclusão: em vez disso.)
user
346

Eu pensei em acrescentar outra possibilidade. Uma das coisas que você pode fazer é conectar duas cenas em um storyboard usando um segue que não está anexado a uma ação e, em seguida, disparar programaticamente o segue dentro do seu controlador de exibição. A maneira como você faz isso é que você deve arrastar do ícone do proprietário do arquivo na parte inferior da cena do storyboard que é a cena seguinte e arrastar com o botão direito do mouse para a cena de destino. Vou colocar uma imagem para ajudar a explicar.

insira a descrição da imagem aqui

Um pop-up será exibido para "Segmento manual". Eu escolhi Push como o tipo. Toque no pequeno quadrado e verifique se você está no inspetor de atributos. Dê a ele um identificador que você usará para se referir a ele no código.

insira a descrição da imagem aqui

Ok, a seguir vou seguir usando um item de botão de barra programática. No viewDidLoad ou em outro lugar, criarei um item de botão na barra de navegação com este código:

UIBarButtonItem *buttonizeButton = [[UIBarButtonItem alloc] initWithTitle:@"Buttonize"
                                                                    style:UIBarButtonItemStyleDone
                                                                   target:self
                                                                   action:@selector(buttonizeButtonTap:)];
self.navigationItem.rightBarButtonItems = @[buttonizeButton];

Ok, observe que o seletor é buttonizeButtonTap :. Portanto, escreva um método nulo para esse botão e, dentro desse método, você chamará o seguinte assim:

-(void)buttonizeButtonTap:(id)sender{
    [self performSegueWithIdentifier:@"Associate" sender:sender];
    }

O parâmetro sender é necessário para identificar o botão quando o prepareForSegue é chamado. prepareForSegue é o método de estrutura em que você instancia sua cena e passa a ela quaisquer valores necessários para realizar seu trabalho. Aqui está a aparência do meu método:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"Associate"])
    {
        TranslationQuizAssociateVC *translationQuizAssociateVC = [segue destinationViewController];
        translationQuizAssociateVC.nodeID = self.nodeID; //--pass nodeID from ViewNodeViewController
        translationQuizAssociateVC.contentID = self.contentID;
        translationQuizAssociateVC.index = self.index;
        translationQuizAssociateVC.content = self.content;
    }
}

Ok, apenas testei e funciona. Espero que ajude você.

smileBot
fonte
@MichaelRowe como isso elimina a necessidade de seguidores? A meu ver você ainda tem que arrastar e soltar no Storyboard para o controlador de destino ..
aherrick
@MichaelRowe isso não elimina a necessidade de segues. O que isso faz é permitir que você siga entre os controladores de exibição que são construídos no código, e não no construtor de interface.
Matthew
@ Matt ele realmente apenas ir me completamente repensar como eu configurar meu aplicativo ... Depois de uma reescrita completa de toda a interface do usuário eu já não usar nenhum segues ..
Michael Rowe
@cocoanut eu estou recebendo o erro como "Aplicação tentou apresentar de forma modal um controlador ativo" qualquer ajuda em relação a este ..
Bala
1
O segmento manual "Push" está obsoleto, use "Show". Esta resposta tem mais detalhes. @smileBot atualize a resposta.
NABbas # 15/16
81

Eu tenho usado esse código para instanciar minha subclasse de segue personalizada e executá-lo programaticamente. Parece funcionar. Algo errado com isso? Estou intrigado, lendo todas as outras respostas dizendo que isso não pode ser feito.

UIViewController *toViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"OtherViewControllerId"];
MyCustomSegue *segue = [[MyCustomSegue alloc] initWithIdentifier:@"" source:self destination:toViewController];
[self prepareForSegue:segue sender:sender];
[segue perform];
Jean-Philippe Pellet
fonte
4
O que há no MyCustomSegue?
Victor Engel
3
É uma subclasse personalizada de UIStoryboardSegue.
Jean-Philippe Pellet
7
@ MarkAmery Muitas pessoas (inclusive eu) evitam usar storyboards. Eles são difíceis de mesclar, e não há verificação em tempo de compilação de que o ID para o qual estou passando performSegueWithIdentifier:esteja realmente definido no storyboard. Eu evito todos os problemas se eu mesmo criar o segue.
Jean-Philippe Pellet
3
Eu concordo com Jean-Philippe. Gerenciar storyboard é uma dor de cabeça. É claro que é fácil clicar em criar poucas visualizações e adicionar um segue aqui e um segue lá, mas gerenciar 6 visualizações com 16 seguidas definidas em XML, quando você tem três desenvolvedores brincando com isso, é terrível. De qualquer forma, o ponto é: o código fornece controle, o xml gerado pelo xcode não.
Krystian
3
Vejo uma falha no [segue perform] no iOS7, não tenho certeza se mais alguém está passando por isso.
Eric Chen
45

Acho que isso foi respondido e aceito, mas eu gostaria de acrescentar mais alguns detalhes.

O que eu fiz para resolver um problema em que eu apresentaria uma visualização de login como primeira tela e, em seguida, queria seguir para o aplicativo se o login estivesse correto. Eu criei o segue do controlador de visualização de login para o controlador de visualização raiz e dei a ele um identificador como "myidentifier".

Depois de verificar todo o código de login, se o login estivesse correto, eu ligaria

[self performSegueWithIdentifier: @"myidentifier" sender: self];

Meu maior mal-entendido foi que eu tentei colocar os seguimentos em um botão e meio que interrompi os seguidos assim que foram encontrados.

qrikko
fonte
4
Como escrevi como outro comentário: venho criando e executando sequências personalizadas programaticamente (veja minha resposta).
Jean-Philippe Pellet
32

Você precisa vincular seu código ao UIStoryboardque está usando. Certifique-se de acessar o YourViewController no seu UIStoryboard, clique na borda ao redor e defina seu identifiercampo como NSStringaquele que você chama no seu código.

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" 
                                                     bundle:nil];
YourViewController *yourViewController = 
 (YourViewController *)
  [storyboard instantiateViewControllerWithIdentifier:@"yourViewControllerID"];
[self.navigationController pushViewController:yourViewController animated:YES];
Jeff Grimes
fonte
1
Entendi isso, mas e se o viewController que eu quero apresentar estiver incorporado em um NavigationController no storyboard? Pelo que posso encontrar, posso inicializar um NavigationController para incorporá-lo, mas no storyboard, já tenho a configuração de push push para a exibição que precisa ser apresentada.
jhilgert00
1
Você pode elaborar sobre isso? Eu acho que este é o problema que estou tendo, mas não consigo encontrar t como / onde para fazer isso ...
jesses.co.tt
1
Mesmo esta solução é correta, trata-se de evitar qualquer seguimento, mas a questão é sobre segue. Dessa forma, você pode conectar ou fazer uma transição entre duas cenas SEM seguir nos storyboards.
BootMaker 17/10
16

Para controladores que estão no storyboard.

jhilgert00 não é isso que você estava procurando?

-(IBAction)nav_goHome:(id)sender {
UIViewController *myController = [self.storyboard instantiateViewControllerWithIdentifier:@"HomeController"];
[self.navigationController pushViewController: myController animated:YES];

}

OU...

[self performSegueWithIdentifier:@"loginMainSegue" sender:self];
user1723341
fonte
3

Eu gostaria de adicionar um esclarecimento ...

Um mal-entendido comum, de fato um que eu tive durante algum tempo, é que um storyboard segue é acionado pelo prepareForSegue:sender:método. Não é. Um storyboard segue será executado, independentemente de você ter implementado um prepareForSegue:sender:método para esse (saindo de) controlador de exibição.

Aprendi isso com as excelentes palestras do iTunesU de Paul Hegarty . Minhas desculpas, mas infelizmente não consigo lembrar qual palestra.

Se você conectar um segue entre dois controladores de exibição em um storyboard, mas não implementar um prepareForSegue:sender: método, o segue continuará no controlador de vista de destino. No entanto, ele segue para esse controlador de exibição despreparado.

Espero que isto ajude.

andrewbuilder
fonte
3

Os segmentos do storyboard não devem ser criados fora do storyboard. Você precisará conectá-lo, apesar das desvantagens.

A referência UIStoryboardSegue afirma claramente:

Você não cria objetos segue diretamente. Em vez disso, o tempo de execução do storyboard os cria quando deve executar uma sequência entre dois controladores de exibição. Você ainda pode iniciar um programa programaticamente usando o método performSegueWithIdentifier: sender: de UIViewController, se desejar. Você pode fazer isso para iniciar um segue de uma fonte que foi adicionada programaticamente e, portanto, não está disponível no Interface Builder.

Você ainda pode dizer programaticamente ao storyboard para apresentar um controlador de exibição usando um segue usando presentModalViewController:ou pushViewController:animated:calls, mas precisará de uma instância do storyboard.

Você pode chamar UIStoryboardo método de classe s para obter um storyboard nomeado com pacote nulo para o pacote principal.

storyboardWithName:bundle:

Cameron Lowell Palmer
fonte
2

Primeiro, suponha que você tenha duas visualizações diferentes no storyboard e deseje navegar de uma tela para outra, então siga estas etapas:

1) Defina todas as suas visualizações com o arquivo de classe e também a identificação do storyboard no inspetor de identidade.

2) Certifique-se de adicionar um controlador de navegação à primeira exibição. Selecione-o no Storyboard e, em seguida, em Editor> Incorporar> Controlador de navegação

3) Na sua primeira classe, importe o "secondClass.h"

#import "ViewController.h
#import "secondController.h"

4) Adicione este comando no IBAction que deve executar o seguinte

secondController *next=[self.storyboard instantiateViewControllerWithIdentifier:@"second"];
[self.navigationController pushViewController:next animated:YES];

5) @"second"é a classe do controlador secondview, ID do storyboard.

Sanket Chauhan
fonte
self.storyboarddeve ser:UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
masipcat 15/03/17
@masipcat eo nome story board pode depender de como você configurar seu projeto Xcode, na minha era "Main.storyboard" então eu useistoryboardWithName:@"Main"
Ammianus
@ Sanket-chauhan se o seu primeiro controlador não está incorporado em um controlador de navegação, você também pode mostrar a próxima exibição usando [self showDetailViewController:next sender:self];ou[self showViewController:next sender:self];
Ammianus
1

Fiz a engenharia reversa e fiz uma (re) implementação de código aberto das sequências do UIStoryboard: https://github.com/acoomans/Segway

Com essa biblioteca, você pode definir segues programaticamente (sem nenhum storyboard).

Espero que ajude.

acoomans
fonte
0

Alguns problemas, na verdade:

Primeiro, nesse projeto que você enviou para nós, o segue não possui o identificador "segue1":

nenhum identificador

Você deve preencher esse identificador, se ainda não o tiver.

Segundo, ao passar da exibição de tabela para exibição de tabela, você está chamando initWithNibName para criar um controlador de exibição. Você realmente deseja usar o instantiateViewControllerWithIdentifier.

jaydip
fonte
0

Aqui está o exemplo de código para Creating a segue programmatically:

class ViewController: UIViewController {
    ...
    // 1. Define the Segue
    private var commonSegue: UIStoryboardSegue!
    ...
    override func viewDidLoad() {
        ...
        // 2. Initialize the Segue
        self.commonSegue = UIStoryboardSegue(identifier: "CommonSegue", source: ..., destination: ...) {
            self.commonSegue.source.showDetailViewController(self.commonSegue.destination, sender: self)
        }
        ...
    }
    ...
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // 4. Prepare to perform the Segue
        if self.commonSegue == segue {
            ...
        }
        ...
    }
    ...
    func actionFunction() {
        // 3. Perform the Segue
        self.prepare(for: self.commonSegue, sender: self)
        self.commonSegue.perform()
    }
    ...
}
jqgsninimo
fonte
Você está ligando self.prepare(for: self.commonSegue, sender: self)do seu método de ação. Então, qual é o sentido de comparar if self.commonSegue == segue {...}no prepare(for:sender)método?
Nayem 3/07
@nayem: In prepare(for:sender:), você pode configurar o controlador de exibição de destino antes de ser exibido. Claro que você também pode fazê-lo actionFunction.
Jqgsninimo 3/07
@nayem: A razão pela qual faço isso é tentar ser consistente com o manuseio de outros seguidores.
Jqgsninimo