Pergunta

Esta pode ser uma pergunta muito simples, mas não produziu nenhum resultado ao pesquisá-la, então aqui está...

Estou tentando descobrir uma maneira de verificar se um determinado controlador de visualização pode executar uma sequência com o identificador XYZ antes de chamar o performSegueWithIdentifier: método.

Algo como:

if ([self canPerformSegueWithIdentifier:@"SegueID"])
    [self performSegueWithIdentifier:@"SegueID"];

Possível?

Foi útil?

Solução

Conforme declarado na documentação:

Os aplicativos normalmente não precisam acionar segues diretamente.Em vez disso, você configura um objeto no construtor de interface associado ao controlador de exibição, como um controle incorporado em sua hierarquia de visualização, para acionar o Segue.No entanto, você pode chamar esse método para acionar um segue programaticamente, talvez em resposta a alguma ação que não possa ser especificada no arquivo de recursos do storyboard.Por exemplo, você pode chamá -lo de um manipulador de ação personalizado usado para processar eventos de agitação ou acelerômetro.

O controlador de exibição que recebe esta mensagem deve ter sido carregado de um storyboard.Se o controlador de visualização não tiver um storyboard associado, talvez porque você o alocou e inicializasse, esse método lança uma exceção.

Dito isto, quando você aciona o segue, normalmente é porque se presume que o UIViewController será capaz de responder a isso com um específico segue's identificador.Também concordo com Dan F, você deve tentar evitar situações em que uma exceção possa ser lançada.Como motivo para você não poder fazer algo assim:

if ([self canPerformSegueWithIdentifier:@"SegueID"])
    [self performSegueWithIdentifier:@"SegueID"];

Eu estou supondo que:

  1. respondsToSelector: apenas verifica se você é capaz de lidar com essa mensagem em tempo de execução.Neste caso você pode, porque a classe UIViewController é capaz de responder performSegueWithIdentifier:sender:.Para realmente verificar se um método é capaz de lidar com uma mensagem com determinados parâmetros, acho que seria impossível, porque para determinar se é possível é necessário realmente executá-lo e, ao fazer isso, o NSInvalidArgumentException irá crescer.
  2. Para realmente criar o que você sugeriu, seria útil receber uma lista de IDs de segue que o UIViewController está associado.De UIViewController documentação, não consegui encontrar nada parecido com isso

Por enquanto, acho que sua melhor aposta é continuar com o @try @catch @finally.

Outras dicas

Para verificar se a segue existia ou não, simplesmente cerquei a chamada com um bloco try-and-catch.Por favor, veja o exemplo de código abaixo:

@try {
    [self performSegueWithIdentifier:[dictionary valueForKey:@"segue"] sender:self];
}
@catch (NSException *exception) {
    NSLog(@"Segue not found: %@", exception);
}

Espero que isto ajude.

- (BOOL)canPerformSegueWithIdentifier:(NSString *)identifier
{
    NSArray *segueTemplates = [self valueForKey:@"storyboardSegueTemplates"];
    NSArray *filteredArray = [segueTemplates filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"identifier = %@", identifier]];
    return filteredArray.count>0;
}

Esta postagem foi atualizada para Swift 4.


Aqui está um mais correto Maneira rápida de verificar se existe uma segue:

extension UIViewController {
func canPerformSegue(withIdentifier id: String) -> Bool {
        guard let segues = self.value(forKey: "storyboardSegueTemplates") as? [NSObject] else { return false }
        return segues.first { $0.value(forKey: "identifier") as? String == id } != nil
    }

    /// Performs segue with passed identifier, if self can perform it.
    func performSegueIfPossible(id: String?, sender: AnyObject? = nil) {
        guard let id = id, canPerformSegue(withIdentifier: id) else { return }
        self.performSegue(withIdentifier: id, sender: sender)
    }
}

// 1
if canPerformSegue("test") {
    performSegueIfPossible(id: "test") // or with sender: , sender: ...)
}

// 2
performSegueIfPossible(id: "test") // or with sender: , sender: ...)

Você pode substituir o -(BOOL)shouldPerformSegueWithIdentifier:sender:método e faça sua lógica lá.

- (BOOL) shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
    if ([identifier isEqualToString:@"someSegue"]) {
        if (!canIPerformSegue) {
            return NO;
        }
    }
    return YES;    
}

Espero que isto ajude.

Referência CanPerformSegue.swift

import UIKit

extension UIViewController{
    func canPerformSegue(identifier: String) -> Bool {
        guard let identifiers = value(forKey: "storyboardSegueTemplates") as? [NSObject] else {
            return false
        }
        let canPerform = identifiers.contains { (object) -> Bool in
            if let id = object.value(forKey: "_identifier") as? String {
                return id == identifier
            }else{
                return false
            }
        }
        return canPerform
    }
}

Versão rápida da resposta de Evgeny Mikhaylov, que funcionou para mim:

Eu reutilizo um controlador para duas visualizações.Isso me ajuda a reutilizar o código.

if(canPerformSegueWithIdentifier("segueFoo")) {
  self.performSegueWithIdentifier("segueFoo", sender: nil)
}
else {
  self.performSegueWithIdentifier("segueBar", sender: nil)
}


func canPerformSegueWithIdentifier(identifier: NSString) -> Bool {
    let templates:NSArray = self.valueForKey("storyboardSegueTemplates") as! NSArray
    let predicate:NSPredicate = NSPredicate(format: "identifier=%@", identifier)

    let filteredtemplates = templates.filteredArrayUsingPredicate(predicate)
    return (filteredtemplates.count>0)
}

Não há como verificar isso usando as funções padrão, o que você pode fazer é subclassificar UIStoryboardSegue e armazenar as informações no source view controller (que é passado para o construtor).No construtor de interface, selecione "Personalizado" como o tipo segue e digite o nome da classe do seu segue, então seu construtor será chamado para cada segue instanciado e você poderá consultar os dados armazenados, se existirem.

Você também deve substituir o perform método para chamar [source presentModalViewController:destination animated:YES] ou [source pushViewController:destination animated:YES] dependendo do tipo de transição que você deseja.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top