Предотвратить переход в методе prepareForSegue?


249

Можно ли отменить переход в prepareForSegue:методе?

Я хочу выполнить некоторую проверку перед переходом, и если условие не выполнено (в этом случае, если какое-либо из UITextFieldних пустое), отобразить сообщение об ошибке вместо выполнения перехода.

Ответы:


485

Это возможно в iOS 6 и более поздних версиях: необходимо реализовать метод

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

На ваш взгляд контроллер. Вы делаете свою проверку там, и если все в порядке, то return YES;если нет, то return NO;и метод prepareForSegue не вызывается.

Обратите внимание, что этот метод не вызывается автоматически при программном запуске сегментов. Если вам нужно выполнить проверку, то вы должны вызвать shouldPerformSegueWithIdentifier, чтобы определить, следует ли выполнять переход.


106
К вашему сведению, если переход запускается программно, вызывая [self executeSegueWithIdentifier: @ "segueIdentifier" sender: nil]; shouldPerformSegueWithIdentifier никогда не будет вызван.
Чувак

3
@ Thedude спасибо, что указал на это. Выслеживал проблему, и она не достигла моей точки останова. Для любого любопытного вам просто нужно вызвать этот метод, заключенный в оператор if, чтобы получить тот же результат.
jpittman

1
@jpittman Не могли бы вы объяснить, что вы имеете в виду, заключенный в утверждение if?
Бода Тальо

7
@AubadaTaljo: (извиняюсь за форматирование) if ([self shouldPerformSegueWithIdentifier:@"segueIdentifier" sender:nil]) { [self performSegueWithIdentifier:@"segueIdentifier" sender:nil]; }
TimMedcalf

Попробовал это в iOS 11.3 SDK из сюжета раскадровки, и «shouldPerformSegueWithIdentifier» вызывался автоматически
Menno

52

Примечание: принятый ответ является наилучшим подходом, если вы можете настроить таргетинг на iOS 6. Для таргетинга на iOS 5 этот ответ подойдет.

Я не верю, что можно отменить переход prepareForSegue. Я бы предложил перенести вашу логику так, чтобы performSegueсообщение было отправлено первым.

Если вы используете Interface Builder для соединения перехода непосредственно с элементом управления (например, связывание перехода непосредственно с a UIButton), то вы можете выполнить это с помощью небольшого рефакторинга. Подключите переход к контроллеру представления вместо определенного элемента управления (удалите старую ссылку перехода, а затем перетащите элемент управления из самого контроллера представления в целевой контроллер представления). Затем создайте IBActionв своем контроллере представления и подключите элемент управления к IBAction. Затем вы можете выполнить свою логику (проверить наличие пустого TextField) в IBAction, который вы только что создали, и решить, делать это или нет performSegueWithIdentifierпрограммно.


Если переход к контроллеру popover, вы не хотите, чтобы второе нажатие кнопки создало другой контроллер popover; правильная вещь, чтобы сделать в этом случае, должна отклонить поповер. Ваш ответ учитывает это правильное поведение. Если вы подключите его прямо с кнопки на раскадровке, я не вижу, как это сделать.
wcochran

1
После нескольких разочаровывающих часов попыток заставить многократные поп-игры, основанные на segue, хорошо играть вместе, я сдался и избавился от последствий поповера в пользу этого решения. На самом деле он использует меньше кода.
mpemburn

Разве это не победило бы цель иметь segues?
Кристик

Факт связывания ViewController с ViewController решил мою проблему. Спасибо! Это лучшее решение
д-р 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 «editChanged» к событию на панели управления представлением

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

«С другой стороны, предлагать кнопку, которую пользователь не должен нажимать, несколько плохо». Я бы не согласился с этим - это отчасти верно, но на самом деле зависит от контекста. Это также плохое поведение - не направлять пользователя - например, он может нажать кнопку, и система объяснит, что нужно сделать в первую очередь. С отключенной или невидимой кнопкой пользователи могут потеряться или позвонить в
службу

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

5

Должен выполнить Sege для входа в систему Регистрация

-(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, а также отключить содержимое ячейки.

Например, у меня есть форма в сгруппированном табличном представлении. Одна из ячеек ведет к другому 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

Другой способ - переопределить метод tableView с помощью willSelectRowAt и вернуть nil, если вы не хотите показывать результат. showDetails()- это какая-то глупость. В большинстве случаев должна быть реализована модель данных, представленная в ячейке с indexPath.

 func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if showDetails() {
                return indexPath            
        }
        return nil
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.