Как проверить, может ли контроллер представления выполнить - PullRequest
34 голосов
/ 09 марта 2012

Это может быть очень простой вопрос, но он не дал результатов при его поиске, так что вот оно ...

Я пытаюсь найти способ проверить, может ли определенный контроллер представления выполнить переход с идентификатором XYZ перед вызовом метода performSegueWithIdentifier:.

Что-то вроде:

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

возможно?

Ответы [ 8 ]

24 голосов
/ 15 февраля 2013

Чтобы проверить, существует ли переход, я просто окружил вызов блоком try-and-catch. Пожалуйста, посмотрите пример кода ниже:

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

Надеюсь, это поможет.

14 голосов
/ 11 июня 2015
- (BOOL)canPerformSegueWithIdentifier:(NSString *)identifier
{
    NSArray *segueTemplates = [self valueForKey:@"storyboardSegueTemplates"];
    NSArray *filteredArray = [segueTemplates filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"identifier = %@", identifier]];
    return filteredArray.count>0;
}
11 голосов
/ 05 октября 2015

Это сообщение было обновлено для Swift 4.


Вот более правильный Быстрый способ проверить, существует ли переход:

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: ...)
6 голосов
/ 28 июня 2012

Как указано в документации:

Приложениям обычно не нужно запускать сегменты напрямую. Вместо этого вы настраиваете объект в Интерфейсном Разработчике, связанный с контроллер представления, такой как элемент управления, встроенный в его иерархию представлений, чтобы вызвать переход. Тем не менее, вы можете вызвать этот метод, чтобы вызвать Программно, возможно, в ответ на некоторые действия, которые не могут быть указан в файле ресурсов раскадровки. Например, вы можете вызовите его из обработчика пользовательских действий, используемого для обработки встряхивания или события акселерометра.

Контроллер представления, который получает это сообщение, должен быть загружен из раскадровки. Если у контроллера представления нет связанного раскадровка, возможно, потому что вы выделили и инициализировали ее самостоятельно, этот метод вызывает исключение.

Как говорится, когда вы запускаете segue, обычно это потому, что предполагается, что UIViewController сможет ответить на него определенным идентификатором segue's. Я также согласен с Дэном Ф., вам следует избегать ситуаций, в которых может возникнуть исключение. Как причина, по которой вы не можете сделать что-то вроде этого:

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

Я предполагаю, что:

  1. respondsToSelector: только проверяет, можете ли вы обработать это сообщение во время выполнения. В этом случае вы можете, потому что класс UIViewController может ответить на performSegueWithIdentifier:sender:. На самом деле, чтобы проверить, способен ли метод обрабатывать сообщение с определенными параметрами, я думаю, это было бы невозможно, потому что для определения, возможно ли это, он действительно должен его запустить, и при этом NSInvalidArgumentException будет расти.
  2. Чтобы создать то, что вы предложили, было бы полезно получить список идентификаторов segue, с которыми связан UIViewController. Из UIViewController документации я не смог найти ничего похожего на это

На данный момент, я думаю, вам лучше всего продолжать с @try @catch @finally.

4 голосов
/ 23 марта 2015

Вы можете переопределить метод - (BOOL) shouldPerformSegueWithIdentifier: sender: и выполнить свою логику там.

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

Надеюсь, это поможет.

2 голосов
/ 29 октября 2018

Ссылка 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
    }
}
1 голос
/ 03 сентября 2015

Быстрая версия ответа Евгения Михайлова, которая работала для меня:

Я повторно использую контроллер для двух представлений.Это помогает мне повторно использовать код.

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)
}
0 голосов
/ 28 июня 2012

Невозможно проверить, используя стандартные функции, что вы можете сделать, это подкласс UIStoryboardSegue и сохранить информацию в контроллере представления source (который передается конструктору).В конструкторе интерфейса выберите «Пользовательский» в качестве типа сегмента и введите имя класса вашего сегмента, тогда ваш конструктор будет вызываться для каждого экземпляра сегмента, и вы можете запросить сохраненные данные, если они существуют.

Вы также должныпереопределите метод perform для вызова [source presentModalViewController:destination animated:YES] или [source pushViewController:destination animated:YES] в зависимости от того, какой тип перехода вы хотите.

...