Segue in der prepareForSegue-Methode verhindern?

249

Ist es möglich, einen Übergang in der prepareForSegue:Methode abzubrechen ?

Ich möchte vor dem Segue eine Prüfung durchführen. Wenn die Bedingung nicht erfüllt ist (in diesem Fall, wenn einige UITextFieldleer sind), wird anstelle des Segues eine Fehlermeldung angezeigt.

Shmidt
quelle

Antworten:

485

In iOS 6 und höher ist dies möglich: Sie müssen die Methode implementieren

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

In Ihrem View Controller. Dort führen Sie Ihre Validierung durch. Wenn dies in Ordnung ist, wird das prepareForSegue nicht aufgerufen, return YES;wenn dies nicht return NO;der Fall ist.

Beachten Sie, dass diese Methode beim programmgesteuerten Auslösen von Segues nicht automatisch aufgerufen wird. Wenn Sie die Prüfung durchführen müssen, müssen Sie shouldPerformSegueWithIdentifier aufrufen, um zu bestimmen, ob eine Übergabe durchgeführt werden soll.

Abraham
quelle
106
Zu Ihrer Information, wenn der Übergang programmgesteuert durch Aufrufen von [self performSegueWithIdentifier: @ "segueIdentifier" sender: nil] ausgelöst wird; shouldPerformSegueWithIdentifier wird niemals aufgerufen.
Der Typ
3
@ Thedude danke für den Hinweis. Ich habe ein Problem aufgespürt und es hat meinen Haltepunkt nicht erreicht. Für alle Neugierigen müssen Sie diese Methode nur in einer if-Anweisung aufrufen, um das gleiche Ergebnis zu erzielen.
Jpittman
1
@jpittman Würden Sie bitte erklären, was Sie mit einer if-Anweisung meinen?
Boda Taljo
7
@ AubadaTaljo: (Entschuldigung für die Formatierung) if ([self shouldPerformSegueWithIdentifier:@"segueIdentifier" sender:nil]) { [self performSegueWithIdentifier:@"segueIdentifier" sender:nil]; }
TimMedcalf
Versuchte dies in iOS 11.3 SDK aus einem Storyboard-Abschnitt und "shouldPerformSegueWithIdentifier" wurde automatisch aufgerufen
Menno
52

Hinweis: Die akzeptierte Antwort ist der beste Ansatz, wenn Sie auf iOS 6 abzielen können. Für die Ausrichtung auf iOS 5 reicht diese Antwort aus.

Ich glaube nicht, dass es möglich ist, einen Übergang abzusagen prepareForSegue. Ich würde vorschlagen, Ihre Logik an den Punkt zu verschieben, an dem die performSegueNachricht zum ersten Mal gesendet wird.

Wenn Sie Interface Builder verwenden, um einen Segue direkt mit einem Steuerelement zu verbinden (z. B. einen Segue direkt mit einem zu verknüpfen UIButton), können Sie dies mit ein wenig Refactoring erreichen. Verbinden Sie den Segue mit dem Ansichts-Controller anstelle eines bestimmten Steuerelements (löschen Sie den alten Segue-Link und ziehen Sie ihn mit gedrückter Ctrl-Taste vom View-Controller selbst zum Ziel-View-Controller). Erstellen Sie dann einen IBActionin Ihrer Ansicht befindlichen Controller und verbinden Sie das Steuerelement mit der IBAction. Anschließend können Sie in der soeben erstellten IBAction Ihre Logik ausführen (auf leeres TextField prüfen) und dort entscheiden, ob Sie performSegueWithIdentifierprogrammgesteuert arbeiten möchten oder nicht .

Mike Mertsock
quelle
Wenn es sich um einen Popover-Controller handelt, möchten Sie nicht durch erneutes Tippen auf die Schaltfläche einen weiteren Popover-Controller erstellen. In diesem Fall ist es richtig, das Popover zu schließen. Ihre Antwort ermöglicht dieses richtige Verhalten. Wenn Sie es direkt über die Schaltfläche im Storyboard verkabeln, sehe ich sowieso nicht das richtige Verhalten.
Wcochran
1
Nachdem ich mehrere frustrierende Stunden lang versucht hatte, mehrere Popover auf Segue-Basis zum guten Zusammenspiel zu bringen, gab ich auf und entfernte die Popover-Sequenzen zugunsten dieser Lösung. Es wird tatsächlich weniger Code verwendet.
mpemburn
Würde dies nicht den Zweck von Segues zunichte machen?
Cristik
Die Tatsache, ViewController mit ViewController zu verknüpfen, löste mein Problem. Danke dir! Dies ist die beste Lösung
Dr. TJ
19

Swift 3 : func shouldPerformSegue (withIdentifier-ID: String, Absender: Beliebig?) -> Bool

Rückgabewert true, wenn die Übergabe durchgeführt werden soll, oder false, wenn sie ignoriert werden soll.

Beispiel :

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
}
OrdoDei
quelle
12

Alternativ ist es etwas schlecht, eine Schaltfläche anzubieten, die ein Benutzer nicht drücken sollte. Sie können den Segue als Ständer verkabelt lassen, aber mit deaktivierter Taste beginnen. Verbinden Sie dann das "editingChanged" des UITextField mit einem Ereignis im Ansichtssteuerelement

- (IBAction)nameChanged:(id)sender {
    UITextField *text = (UITextField*)sender;
    [nextButton setEnabled:(text.text.length != 0)];
}
Kaolin Feuer
quelle
"Alternativ ist es etwas schlecht, eine Schaltfläche anzubieten, die ein Benutzer nicht drücken sollte." Ich würde dem nicht zustimmen - dies ist teilweise wahr, hängt aber wirklich vom Kontext ab. Es ist auch ein schlechtes Verhalten, den Benutzer nicht anzuleiten - zum Beispiel, damit er auf eine Schaltfläche tippen kann und das System erklärt, was zuerst zu tun ist. Mit einer deaktivierten oder unsichtbaren Schaltfläche gehen Benutzer entweder verloren oder rufen den Support an ...
csmith
11

Es ist einfach im Handumdrehen.

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

    return true
}
Zumry Mohamed
quelle
3
Huh? Können Sie diese Antwort näher erläutern? Nur-Code-Antworten sind für weitere Leser nicht sehr nützlich ...
Cristik
9

Wie Abraham sagte, überprüfen Sie die Gültigkeit oder Nicht-Gültigkeit in der folgenden Funktion.

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

Und das performSegueWithIdentifier:sender:durch Programmieren aufgerufene kann durch Überschreiben der folgenden Methode blockiert werden. Standardmäßig wird nicht überprüft, ob gültig oder nicht gültig -shouldPerformSegueWithIdentifier:sender:ist. Wir können dies manuell tun.

- (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];
}
AechoLiu
quelle
Ist dieser Teil [super performSegueWithIdentifier:identifier sender:sender];wirklich wahr?
Ben Wheeler
@ BenWheeler Sie können es versuchen. Wenn Sie die performSegueWithIdentifier:sender:Methode überschreiben und nicht aufrufen super.
AechoLiu
5

Sollte Segue for Login Register durchführen

-(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);

}
Swappyee
quelle
4

Ähnlich wie bei Kaolin besteht die Antwort darin, die Sequenz mit der Kontrolle verbunden zu lassen, die Kontrolle jedoch anhand der Bedingungen in der Ansicht zu validieren. Wenn Sie auf die Interaktion mit Tabellenzellen zugreifen, müssen Sie auch die Eigenschaft userInteractionEnabled festlegen und die Inhalte in der Zelle deaktivieren.

Zum Beispiel habe ich ein Formular in einer gruppierten Tabellenansicht. Eine der Zellen führt zu einer anderen tableView, die als Auswahl fungiert. Immer wenn ein Steuerelement in der Hauptansicht geändert wird, rufe ich diese Methode auf

-(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;
    }

}
James Moore
quelle
4

Swift 4 Antwort:

Es folgt die Implementierung von Swift 4, um den Übergang abzubrechen:

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
}
Pankaj Kulkarni
quelle
2

Die andere Möglichkeit besteht darin, die Methode von tableView mit willSelectRowAt zu überschreiben und nil zurückzugeben, wenn Sie den Übergang nicht anzeigen möchten. showDetails()- Ist ein Idiot. In den meisten Fällen sollte in Datenmodell implementiert werden, das in Zelle mit dargestellt wird indexPath.

 func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if showDetails() {
                return indexPath            
        }
        return nil
    }
Bogdan Ustyak
quelle