2012-03-08 3 views
32

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

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

Что-то вдоль линий:

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

возможно?

+0

Именно этот вопрос я ищу ответ на в данный момент ... –

+1

Hey Dan I закончил с помощью '@try @catch @ finally'. Он работает нормально. – Rog

+0

Я тоже, я просто очень надеюсь, что на самом деле есть способ проверить это. Как правило, я стараюсь избегать ситуаций, когда исключение МОЖЕТ бросать во время обычных ситуаций времени выполнения. –

ответ

7

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

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

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

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

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

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

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

Как сейчас, я предполагаю, что ваш лучший выбор это продолжать идти с @try@catch@finally.

-1

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

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

+0

Вызов настоящего, push и pop программно очень опасен, потому что segue может быть изменен в любое время от модели до нажатой сцены. Когда вы выйдете модальным, он рухнет. Лучше всего быть уверенным, что названный segue существует или использует try/catch, который, я думаю, все еще довольно неряшлив, поскольку он не является действительно исключительным. Должен быть метод проверки определения идентификатора segue. Это, по-видимому, отсутствует. – Brennan

24

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

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

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

+1

Это решение отлично работает. Обратите внимание, что если у вас есть контрольная точка исключения, она все равно сломается. Однако вы можете продолжать без сбоев. – VaporwareWolf

+1

Вы можете получить утечки памяти под ARC, потому что он не очищается после исключения. – Andy

+0

Не используйте это. Поскольку Энди сказал, что это утечка контроллера представления, отправляющего сообщение 'performSegueWithIdentifier: sender:' и весь его контроллер дочернего представления (потому что UIKit сохраняет контроллер вида, но никогда не получает возможности его освободить из-за исключения). –

3

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

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

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

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

Спасибо, это хорошо знать (хотя это недокументированное поведение). – Rog

+0

Я сожалею, что у меня есть только один взлет. Большое использование предикатов, Евгений. –

+0

NB в соответствии с ответом на http://stackoverflow.com/a/35060917/305913, использование этого недокументированного поведения может привести к тому, что ваше приложение будет отклонено из хранилища приложений. –

1

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

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

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

Я считаю, что имя этого метода зарезервировано в iOS, так как я испытываю неожиданное поведение, используя этот подход. Лучше переименовать его, например. to 'canRunSegueWithIdentifier' –

9

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

extension UIViewController { 
    func canPerformSegue(id: String) -> Bool { 
     let segues = self.valueForKey("storyboardSegueTemplates") as? [NSObject] 
     let filtered = segues?.filter({ $0.valueForKey("identifier") as? String == id }) 
     return (filtered?.count > 0) ?? false 
    } 

    // Just so you dont have to check all the time 
    func performSegue(id: String, sender: AnyObject?) -> Bool { 
     if canPerformSegue(id) { 
      self.performSegueWithIdentifier(id, sender: sender) 
      return true 
     } 
     return false 
    } 
} 

// 1 
if canPerformSegue("test") { 
    self.performSegueWithIdentifier("test", sender: nil) 
} 

// 2 
performSegue("test", sender: nil) 
Смежные вопросы