Impedire segue nel metodo preparForSegue?


249

È possibile annullare un seguito nel prepareForSegue:metodo?

Voglio eseguire un controllo prima del seguito, e se la condizione non è vera (in questo caso, se alcuni UITextFieldè vuota), visualizzare un messaggio di errore invece di eseguire il seguito.

Risposte:


485

È possibile in iOS 6 e versioni successive: devi implementare il metodo

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

Nel tuo controller di visualizzazione. Fai la tua validazione lì, e se è OK allora return YES;se non lo è allora return NO;e il readyForSegue non viene chiamato.

Si noti che questo metodo non viene chiamato automaticamente quando si innescano follower a livello di codice. Se è necessario eseguire il controllo, è necessario chiamare shouldPerformSegueWithIdentifier per determinare se eseguire segue.


106
Cordiali saluti, se il seguito viene attivato a livello di codice chiamando [self performSegueWithIdentifier: @ mittente "segueIdentifier": zero]; shouldPerformSegueWithIdentifier non verrà mai chiamato.
Il tizio il

3
@ Grazie grazie per averlo sottolineato. Stava rintracciando un problema e non stava colpendo il mio punto di interruzione. Per chiunque sia curioso, devi solo chiamare questo metodo racchiuso in un'istruzione if per ottenere lo stesso risultato.
jpittman,

1
@jpittman, per favore, spieghi cosa vuoi dire racchiuso in un'istruzione if?
Boda Taljo,

7
@AubadaTaljo: (scuse per la formattazione) if ([self shouldPerformSegueWithIdentifier:@"segueIdentifier" sender:nil]) { [self performSegueWithIdentifier:@"segueIdentifier" sender:nil]; }
TimMedcalf

Ho provato questo in iOS 11.3 SDK da uno storyboard seguito e "shouldPerformSegueWithIdentifier" è stato chiamato automaticamente
Menno

52

Nota: la risposta accettata è l'approccio migliore se puoi scegliere come target iOS 6. Per il targeting iOS 5, questa risposta farà.

Non credo sia possibile annullare un seguito prepareForSegue. Suggerirei di spostare la tua logica al punto in cui il performSeguemessaggio viene inviato per la prima volta.

Se stai usando Interface Builder per collegare un seguito direttamente a un controllo (ad esempio collegando un seguito direttamente a un UIButton), puoi farlo con un po 'di refactoring. Collegare i seguenti al controller della vista anziché a un controllo specifico (eliminare il vecchio collegamento segue, quindi trascinare il controllo dal controller della vista stesso al controller della vista di destinazione). Quindi crea un IBActioncontroller nella vista e collega il controllo a IBAction. Quindi puoi fare la tua logica (controlla TextField vuoto) nell'IBAction che hai appena creato, e decidere lì se performSegueWithIdentifierprogrammarlo.


Se il seguito è per un controller popover, non si desidera un secondo tocco del pulsante per creare un altro controller popover; la cosa corretta da fare in questo caso è chiudere il popover. La tua risposta consente questo comportamento corretto. Se lo installi direttamente dal pulsante nello storyboard, non vedo comunque per ottenere il comportamento corretto.
wcochran,

1
Dopo diverse frustranti ore di tentativi per far sì che più popover basati su Segu suonassero bene insieme, ho rinunciato e mi sono sbarazzato delle sequenze di popover a favore di questa soluzione. In realtà utilizza meno codice.
lunedì

Questa sconfitta non avrebbe lo scopo di avere segues?
Cristik,

Il fatto di collegare ViewController a ViewController ha risolto il mio problema. Grazie! Questa è la soluzione migliore
Dr TJ,

19

Swift 3 : func shouldPerformSegue (withIdentifier identifier: String, mittente: Any?) -> Bool

Restituisce il valore vero se il seguito deve essere eseguito o falso se deve essere ignorato.

Esempio :

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

In alternativa, è un comportamento un po 'brutto offrire un pulsante che un utente non dovrebbe premere. Puoi lasciare il seguito cablato come supporto, ma inizia con il pulsante disabilitato. Quindi collegare "editingChanged" di UITextField a un evento sull'ala di controllo della vista

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

"In alternativa, è un comportamento un po 'brutto offrire un pulsante che un utente non dovrebbe premere". Non sarei d'accordo con questo - questo è parzialmente vero ma dipende davvero dal contesto. È anche un cattivo comportamento non guidare l'utente, ad esempio in modo che possano toccare un pulsante e il sistema spiega cosa deve essere fatto prima. Con un pulsante disabilitato o invisibile gli utenti si perderanno o chiameranno l'assistenza ...
csmith

11

È facile in fretta.

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

    return true
}

3
Eh? Puoi approfondire di più su questa risposta? Le risposte di solo codice non sono molto utili per altri lettori ...
Cristik,

9

Come ha detto Abraham, controlla valido o meno nella seguente funzione.

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

E, il performSegueWithIdentifier:sender:richiamo dalla programmazione può essere bloccato sovrascrivendo il seguente metodo. Per impostazione predefinita, non sta controllando valido o no -shouldPerformSegueWithIdentifier:sender:, possiamo farlo manualmente.

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

questa parte è [super performSegueWithIdentifier:identifier sender:sender];davvero vera?
Ben Wheeler,

@BenWheeler Puoi provarlo. Se si ignora il performSegueWithIdentifier:sender:metodo e non lo si chiama supermetodo.
AechoLiu,

5

Dovrebbe eseguire Segue per il registro di accesso

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

Simile alla risposta di Kaolin è lasciare il seque collegato al controllo ma convalidare il controllo in base alle condizioni nella vista. Se stai attivando l'interazione tra celle della tabella, devi anche impostare la proprietà userInteractionEnabled e disabilitare gli elementi nella cella.

Ad esempio, ho un modulo in una vista tabella raggruppata. Una delle celle conduce a un altro tableView che funge da selettore. Ogni volta che un controllo viene modificato nella vista principale, chiamo questo metodo

-(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 Risposta:

Di seguito è l'implementazione di Swift 4 per annullare segue:

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

L'altro modo è quello di sovrascrivere il metodo di tableView con willSelectRowAt e restituire zero se non si desidera mostrare il seguito. showDetails()- è un po 'bool. Nella maggior parte dei casi dovrebbe essere implementato nel modello di dati rappresentato in cella con indexPath.

 func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if showDetails() {
                return indexPath            
        }
        return nil
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.