Zapobiec segue w metodzie preparForSegue?

249

Czy można anulować segue w prepareForSegue:metodzie?

Chcę wykonać pewne sprawdzenie przed segue, a jeśli warunek nie jest spełniony (w tym przypadku, jeśli niektóre UITextFieldsą puste), zamiast wykonywania segue wyświetl komunikat o błędzie.

Shmidt
źródło

Odpowiedzi:

485

Jest to możliwe w iOS 6 i nowszych: Musisz zaimplementować tę metodę

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

W twoim kontrolerze widoku. Dokonujesz tam walidacji, a jeśli wszystko jest w porządku, to return YES;jeśli nie, to nie return NO;jest wywoływana funkcja PrepForSegue.

Zauważ, że ta metoda nie jest wywoływana automatycznie podczas programowego uruchamiania segu. Jeśli musisz wykonać sprawdzenie, musisz wywołać funkcję shouldPerformSegueWithIdentifier, aby ustalić, czy wykonać segue.

Abraham
źródło
106
Do Twojej wiadomości, jeśli segue jest wywoływany programowo przez wywołanie [self performSegueWithIdentifier: @ "segueIdentifier" nadawca: zero]; shouldPerformSegueWithIdentifier nigdy nie zostanie wywołany.
Koleś
3
@Thedude dzięki za zwrócenie na to uwagi. Wyśledził problem i nie trafił w mój punkt przerwania. Dla każdego, kto jest ciekawy, wystarczy wywołać tę metodę zawartą w instrukcji if, aby uzyskać ten sam wynik.
jpittman,
1
@jpittman, czy mógłbyś wyjaśnić, co masz na myśli, jeśli jest zawarty w instrukcji if?
Boda Taljo
7
@AubadaTaljo: (przeprosiny za formatowanie) if ([self shouldPerformSegueWithIdentifier:@"segueIdentifier" sender:nil]) { [self performSegueWithIdentifier:@"segueIdentifier" sender:nil]; }
TimMedcalf
Próbowałem tego w iOS 11.3 SDK z segmentu scenorysu, a „shouldPerformSegueWithIdentifier” został wywołany automatycznie
Menno
52

Uwaga: zaakceptowana odpowiedź jest najlepszym podejściem, jeśli możesz kierować reklamy na iOS 6. W przypadku targetowania na iOS 5 ta odpowiedź będzie wystarczająca.

Nie sądzę, że można anulować segue w prepareForSegue. Sugerowałbym przeniesienie logiki do punktu, w którym performSeguewiadomość jest wysyłana po raz pierwszy.

Jeśli używasz Konstruktora interfejsów, aby podłączyć segue bezpośrednio do kontrolki (np. Łącząc segue bezpośrednio z a UIButton), możesz to zrobić przy odrobinie refaktoryzacji. Połącz segue z kontrolerem widoku zamiast określonego kontrolki (usuń stare łącze segue, a następnie przeciągnij z samego kontrolera widoku do docelowego kontrolera widoku). Następnie utwórz IBActionkontroler widoku i połącz go z sekcją IBAction. Następnie możesz wykonać swoją logikę (sprawdzić, czy pole TextField jest puste) w utworzonym IBAction i zdecydować, czy performSegueWithIdentifierprogramować.

Mike Mertsock
źródło
Jeśli segue dotyczy kontrolera popover, nie chcesz drugiego stuknięcia przycisku, aby utworzyć inny kontroler popover; właściwą rzeczą do zrobienia w tym przypadku jest zamknięcie programu popover. Twoja odpowiedź pozwala na to właściwe zachowanie. Jeśli podłączysz go bezpośrednio z przycisku w serii ujęć, i tak nie widzę, aby uzyskać prawidłowe zachowanie.
wcochran
1
Po kilku frustrujących godzinach próbowania zdobycia wielu popoverów opartych na segue, aby dobrze się bawili, zrezygnowałem i pozbyłem się sekwencji popover na rzecz tego rozwiązania. W rzeczywistości zużywa mniej kodu.
mpemburn
Czy nie pokonałoby to celu posiadania seguów?
Cristik,
Fakt połączenia ViewController z ViewController rozwiązał mój problem. Dziękuję Ci! To najlepsze rozwiązanie
Dr TJ
19

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

Zwraca wartość true, jeśli należy wykonać segment, lub false, jeśli należy go zignorować.

Przykład :

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
źródło
12

Alternatywnie, nieco złym zachowaniem jest oferowanie przycisku, którego użytkownik nie powinien naciskać. Możesz zostawić segue okablowanym jako stojaki, ale zacznij od wyłączonego przycisku. Następnie połącz „editChanged” UITextField ze zdarzeniem w ala kontroli widoku

- (IBAction)nameChanged:(id)sender {
    UITextField *text = (UITextField*)sender;
    [nextButton setEnabled:(text.text.length != 0)];
}
Kaolin Fire
źródło
„Alternatywnie, nieco złym zachowaniem jest oferowanie przycisku, którego użytkownik nie powinien naciskać”. Nie zgodziłbym się z tym - jest to częściowo prawda, ale tak naprawdę zależy od kontekstu. Niewłaściwe jest również nieprowadzenie użytkownika - na przykład, aby mógł nacisnąć przycisk, a system wyjaśni, co należy zrobić w pierwszej kolejności. Z wyłączonym lub niewidocznym przyciskiem użytkownicy
zgubią
11

Łatwo jest szybko.

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

    return true
}
Zumry Mohamed
źródło
3
Co? Czy możesz rozwinąć tę odpowiedź? Tylko kodowe odpowiedzi nie są zbyt przydatne dla dalszych czytelników ...
Cristik
9

Jak powiedział Abraham, sprawdź poprawność lub nie w następującej funkcji.

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

I performSegueWithIdentifier:sender:wywoływane przez programowanie można zablokować, zastępując następującą metodę. Domyślnie nie sprawdza poprawności lub nie -shouldPerformSegueWithIdentifier:sender:, możemy to zrobić ręcznie.

- (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
źródło
czy ta część jest [super performSegueWithIdentifier:identifier sender:sender];naprawdę prawdziwa?
Ben Wheeler,
@BenWheeler Możesz spróbować. Jeśli zastąpisz performSegueWithIdentifier:sender:metodę, a nie wywołasz jej, to supermetoda.
AechoLiu
5

Powinieneś wykonać Segue dla logowania Zarejestruj się

-(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
źródło
4

Podobnie do odpowiedzi Kaolina jest pozostawienie seque podłączonego do kontroli, ale sprawdzenie poprawności na podstawie warunków w widoku. Jeśli odpalasz interakcję komórki tabeli, musisz również ustawić właściwość userInteractionEnabled, a także wyłączyć rzeczy w komórce.

Na przykład mam formularz w zgrupowanym widoku tabeli. Jedna z komórek prowadzi do innego tableView, który działa jak próbnik. Za każdym razem, gdy kontrola jest zmieniana w głównym widoku, wywołuję tę metodę

-(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
źródło
4

Odpowiedź Swift 4:

Poniżej przedstawiono implementację Swift 4, aby anulować 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
}
Pankaj Kulkarni
źródło
2

Innym sposobem jest zastąpienie metody tableView za pomocą willSelectRowAt i zwrócenie wartości zero, jeśli nie chcesz wyświetlać segue. showDetails()- jest trochę bool. W większości przypadków powinien być zaimplementowany w modelu danych reprezentowanym w komórce za pomocą indexPath.

 func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if showDetails() {
                return indexPath            
        }
        return nil
    }
Bogdan Ustyak
źródło