prepareForSegueメソッドでセグエを防止しますか?


249

prepareForSegue:メソッドでセグエをキャンセルすることは可能ですか?

セグエの前にいくつかのチェックを実行し、条件がtrueでない場合(この場合、一部UITextFieldが空の場合)、セグエを実行する代わりにエラーメッセージを表示します。

回答:


485

iOS 6以降で可能です:メソッドを実装する必要があります

- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender 

あなたのView Controllerで。そこで検証を行います。問題がないreturn YES;場合は、そうでなくreturn NO;、prepareForSegueは呼び出されません。

このメソッドは、プログラムでセグエをトリガーするときに自動的に呼び出されないことに注意してください。チェックを実行する必要がある場合は、seguePerformSegueWithIdentifierを呼び出して、セグエを実行するかどうかを決定する必要があります。


106
参考までに、[self performSegueWithIdentifier:@ "segueIdentifier" sender:nil]を呼び出してプログラムでセグエがトリガーされた場合; shouldPerformSegueWithIdentifierが呼び出されることはありません。

3
@Thedudeこれを指摘してくれてありがとう。問題を追跡していましたが、ブレークポイントに達していませんでした。好奇心旺盛な方は、同じ結果を得るためにifステートメントでラップされたこのメソッドを呼び出すだけです。
jpittman 2013

1
@jpittmanは、ifステートメントにラップされているとはどういう意味ですか?
Boda Taljo 2014

7
@AubadaTaljo:(フォーマットの謝罪) if ([self shouldPerformSegueWithIdentifier:@"segueIdentifier" sender:nil]) { [self performSegueWithIdentifier:@"segueIdentifier" sender:nil]; }
TimMedcalf 14

ストーリーボードセグエからiOS 11.3 SDKでこれを試したところ、「shouldPerformSegueWithIdentifier」が自動的に呼び出されました
Menno

52

注:iOS 6をターゲットにできる場合、受け入れられた回答が最良のアプローチです。iOS5をターゲットにすると、この回答で十分です。

でセグエをキャンセルすることはできないと思いますprepareForSegueperformSegueメッセージが最初に送信されるようにロジックを移動することをお勧めします。

Interface Builderを使用してセグエをコントロールに直接配線する場合(たとえば、セグエをに直接リンクするUIButton場合)、少しのリファクタリングでこれを実現できます。特定のコントロールではなくビューコントローラーにセグエを配線します(古いセグエリンクを削除してから、ビューコントローラー自体から宛先のビューコントローラーにコントロールドラッグします)。次にIBAction、ビューコントローラーでを作成し、コントロールをIBActionにワイヤリングします。次に、作成したIBActionでロジック(空のTextFieldを確認)を実行し、performSegueWithIdentifierプログラムで行うかどうかを決定できます。


セグエがポップオーバーコントローラーに対するものである場合、ボタンを2回タップして別のポップオーバーコントローラーを作成する必要はありません。この場合の正しいことは、ポップオーバーを閉じることです。あなたの答えは、この適切な行動を可能にします。ストーリーボードのボタンから直接配線すると、適切な動作が得られません。
wcochran

1
複数のセグエベースのポップオーバーをうまく一緒にプレイしようとする苛立たしい数時間の後、私はあきらめて、このソリューションを支持してポップオーバーシークを取り除きました。実際には、より少ないコードを使用します。
mpemburn 2013

これは、セグエを持つ目的を無効にしないのではないでしょうか?
クリスティク

ViewControllerをViewControllerにリンクすることで問題が解決しました。ありがとうございました!これが最良の解決策です
Dr TJ

19

Swift 3:func shouldPerformSegue(withIdentifier identifier:String、sender:Any?)-> Bool

戻り値セグエを実行する必要がある場合はtrue、無視する場合はfalse

var badParameters:Bool = true

override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
    if badParameters  {
         // your code here, like badParameters  = false, e.t.c
         return false
    }
    return true
}

12

または、ユーザーが押してはならないボタンを提供するのは、少し悪い動作です。セグエをそのまま配線したままにすることができますが、ボタンを無効にして開始します。次に、UITextFieldの「editingChanged」をビューコントロールalaのイベントにワイヤリングします。

- (IBAction)nameChanged:(id)sender {
    UITextField *text = (UITextField*)sender;
    [nextButton setEnabled:(text.text.length != 0)];
}

「あるいは、ユーザーが押してはならないボタンを提供するのは少し悪い行動です」。私はこれに同意しません-これは部分的に正しいですが、実際にはコンテキストに依存します。また、ユーザーをガイドしないのも悪い行動です。たとえば、ユーザーがボタンをタップして、システムが最初に何をする必要があるかを説明できるようにします。無効または非表示のボタンを使用すると、ユーザーは迷子になるか、サポートに電話をします...
csmith

11

迅速で簡単です。

override func shouldPerformSegueWithIdentifier(identifier: String,sender: AnyObject?) -> Bool {

    return true
}

3
え?この答えについて詳しく説明できますか?コードのみの回答は、これ以上の読者にはあまり役に立ちません...
Cristik

9

アブラハムが言ったように、次の関数で有効かどうかを確認します。

- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(nullable id)sender
{
     // Check this identifier is OK or NOT.
}

また、performSegueWithIdentifier:sender:以下のメソッドを上書きすることで、呼び出し元をブロックすることができます。デフォルトでは、有効かどうかのチェックは行われません-shouldPerformSegueWithIdentifier:sender:。手動で行うことができます。

- (void)performSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
    // Check valid by codes
    if ([self shouldPerformSegueWithIdentifier:identifier sender:sender] == NO) {
        return;
    }

    // If this identifier is OK, call `super` method for `-prepareForSegue:sender:` 
    [super performSegueWithIdentifier:identifier sender:sender];
}

この部分は[super performSegueWithIdentifier:identifier sender:sender];本当に本当ですか?
ベンウィーラー

@BenWheeler試すことができます。performSegueWithIdentifier:sender:メソッドをオーバーライドし、そのメソッドを呼び出さない場合super
AechoLiu 2016

5

ログインレジスタのセグエを実行する必要があります

-(BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{

    [self getDetails];

    if ([identifier isEqualToString:@"loginSegue"])
    {

        if (([_userNameTxtf.text isEqualToString:_uname])&&([_passWordTxtf.text isEqualToString:_upass]))
        {

            _userNameTxtf.text=@"";
            _passWordTxtf.text=@"";

            return YES;
        }
        else
        {
            UIAlertView *loginAlert = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"Invalid Details" delegate:self cancelButtonTitle:@"Try Again" otherButtonTitles:nil];

            [loginAlert show];

            _userNameTxtf.text=@"";
            _passWordTxtf.text=@"";

            return NO;
        }

    }

    return YES;

}

-(void)getDetails
{
    NSArray *dir=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *dbpath=[NSString stringWithFormat:@"%@/userDb.sqlite",[dir lastObject]];

    sqlite3 *db;

    if(sqlite3_open([dbpath UTF8String],&db)!=SQLITE_OK)
    {
        NSLog(@"Fail to open datadbase.....");
        return;
    }

    NSString *query=[NSString stringWithFormat:@"select * from user where userName = \"%@\"",_userNameTxtf.text];

    const char *q=[query UTF8String];

    sqlite3_stmt *mystmt;

    sqlite3_prepare(db, q, -1, &mystmt, NULL);

    while (sqlite3_step(mystmt)==SQLITE_ROW)
    {
        _uname=[NSString stringWithFormat:@"%s",sqlite3_column_text(mystmt, 0)];

        _upass=[NSString stringWithFormat:@"%s",sqlite3_column_text(mystmt, 2)];
    }

    sqlite3_finalize(mystmt);
    sqlite3_close(db);

}

4

カオリンの答えと同様に、シークをコントロールに配線したままにし、ビュー内の条件に基づいてコントロールを検証します。テーブルセルの相互作用で起動している場合は、セルの内容を無効にするだけでなく、userInteractionEnabledプロパティも設定する必要があります。

たとえば、グループ化されたテーブルビューにフォームがあります。セルの1つは、ピッカーとして機能する別のtableViewにつながります。メインビューでコントロールが変更されるたびに、このメソッドを呼び出します

-(void)validateFilterPicker
{
    if (micSwitch.on)
    {
        filterPickerCell.textLabel.enabled = YES;
        filterPickerCell.detailTextLabel.enabled = YES;
        filterPickerCell.userInteractionEnabled = YES;
        filterPickerCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }
    else
    {
        filterPickerCell.textLabel.enabled = NO;
        filterPickerCell.detailTextLabel.enabled = NO;
        filterPickerCell.userInteractionEnabled = NO;
        filterPickerCell.accessoryType = UITableViewCellAccessoryNone;
    }

}

4

Swift 4の回答:

以下は、セグエをキャンセルするためのSwift 4の実装です。

override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
    if identifier == "EditProfile" {
        if userNotLoggedIn {
            // Return false to cancel segue with identified Edit Profile
            return false
        }
    }
    return true
}

2

もう1つの方法は、テーブルビューのメソッドをwillSelectRowAtでオーバーライドし、セグエを表示したくない場合はnilを返すことです。 showDetails()-いくつかのブールです。ほとんどの場合、でセルで表されるデータモデルに実装する必要がありますindexPath

 func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if showDetails() {
                return indexPath            
        }
        return nil
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.