web-dev-qa-db-ja.com

View Controllerがセグエを実行できるかどうかを確認する方法

これは非常に単純な質問かもしれませんが、検索しても結果が得られなかったので、ここにあります...

performSegueWithIdentifier:メソッドを呼び出す前に、特定のビューコントローラが識別子XYZでセグエを実行できるかどうかを確認する方法を模索しています。

以下に沿ったもの:

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

可能?

35
Rog

ドキュメントに記載されているように:

アプリは通常、セグエを直接トリガーする必要はありません。代わりに、ビューコントローラーに関連付けられたオブジェクト(ビュー階層に埋め込まれたコントロールなど)をInterface Builderで構成して、セグエをトリガーします。ただし、このメソッドを呼び出して、おそらくストーリーボードのリソースファイルで指定できないアクションに対応して、プログラムでセグエをトリガーすることができます。たとえば、シェイクまたは加速度計イベントの処理に使用されるカスタムアクションハンドラーから呼び出すことができます。

このメッセージを受け取るView Controllerは、ストーリーボードからロードされている必要があります。ビューコントローラーにストーリーボードが関連付けられていない場合、おそらく自分で割り当てて初期化したため、このメソッドは例外をスローします。

そうは言っても、segueをトリガーするときは、通常、UIViewControllerが特定のsegue's識別子で応答できると想定されているためです。ダンFにも同意します。例外がスローされる可能性のある状況は避けてください。あなたがこのようなことをすることができない理由として:

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

私はそれを推測しています:

  1. respondsToSelector:は、実行時にそのメッセージを処理できるかどうかのみをチェックします。この場合は、クラスUIViewControllerperformSegueWithIdentifier:sender:に応答できるため、可能です。メソッドが特定のパラメーターを使用してメッセージを処理できるかどうかを実際に確認するには、実際に実行する必要があるかどうかを判断するためにNSInvalidArgumentExceptionが上昇するため、不可能だと思います。
  2. あなたが提案したものを実際に作成するには、UIViewControllerが関連付けられているセグエのIDのリストを受け取ると役に立ちます。 UIViewControllerdocumentation から、そのようなものを見つけることができませんでした

今のところ、@try@catch@finallyを使い続けることが最善の策だと思います。

6
Rui Peres

セグエが存在するかどうかを確認するために、呼び出しをtry-and-catchブロックで囲みました。以下のコード例をご覧ください。

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

お役に立てれば。

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

この投稿はSwift 4.に対して更新されています。


これがもっと正しい Swiftセグエが存在するかどうかを確認する方法です:

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: ...)
13
Arbitur

-(BOOL)shouldPerformSegueWithIdentifier:sender:メソッドをオーバーライドして、そこでロジックを実行できます。

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

お役に立てれば。

4
ffxfiend

参照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
    }
}
2
yuelong qin

Evgeny Mikhaylovの回答のSwiftバージョン。

2つのビューでコントローラーを再利用します。これにより、コードを再利用できます。

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)
}
1
JoeGalind