Ngăn chặn segue trong phương thức chuẩn bịForSegue?


249

Có thể hủy bỏ một segue trong prepareForSegue:phương thức?

Tôi muốn thực hiện một số kiểm tra trước segue và nếu điều kiện không đúng (trong trường hợp này, nếu một số UITextFieldtrống), hiển thị thông báo lỗi thay vì thực hiện segue.

Câu trả lời:


485

Có thể có trong iOS 6 trở lên: Bạn phải thực hiện phương pháp

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

Trong bộ điều khiển xem của bạn. Bạn thực hiện xác nhận của mình ở đó, và nếu nó ổn thì return YES;nếu không thì return NO;và Chuẩn bị không được gọi.

Lưu ý rằng phương pháp này không được gọi tự động khi kích hoạt phân tách theo chương trình. Nếu bạn cần thực hiện kiểm tra, thì bạn phải gọi ShouldPerformSegueWithIdentifier để xác định xem có thực hiện segue hay không.


106
FYI, nếu segue được kích hoạt theo chương trình bằng cách gọi [self PerformanceSegueWithIdentifier: @ "segueIdentifier" sender: nil]; ShouldPerformSegueWithIdentifier sẽ không bao giờ được gọi.
chàng

3
@Thedude cảm ơn đã chỉ ra điều này. Đã theo dõi một vấn đề và nó đã không đạt điểm dừng của tôi. Đối với bất kỳ ai tò mò, bạn chỉ cần gọi phương thức này được gói trong một câu lệnh if để có kết quả tương tự.
jpittman

1
@jpittman bạn vui lòng giải thích ý bạn là gì trong một câu lệnh if?
Boda Taljo

7
@AubadaTaljo: (xin lỗi vì định dạng) if ([self shouldPerformSegueWithIdentifier:@"segueIdentifier" sender:nil]) { [self performSegueWithIdentifier:@"segueIdentifier" sender:nil]; }
TimMedcalf

Đã thử điều này trong SDK iOS 11.3 từ một bảng phân cảnh và "ShouldPerformSegueWithIdentifier" đã được gọi tự động
Menno

52

Lưu ý: câu trả lời được chấp nhận là cách tiếp cận tốt nhất nếu bạn có thể nhắm mục tiêu iOS 6. Để nhắm mục tiêu iOS 5, câu trả lời này sẽ làm.

Tôi không tin rằng có thể hủy bỏ một segue trong prepareForSegue. Tôi sẽ đề nghị chuyển logic của bạn đến điểm mà performSeguetin nhắn được gửi đầu tiên.

Nếu bạn đang sử dụng Trình tạo giao diện để kết nối trực tiếp một điều khiển với điều khiển (ví dụ: liên kết trực tiếp một câu lệnh với a UIButton), thì bạn có thể thực hiện việc này với một chút tái cấu trúc. Nối dây segue vào bộ điều khiển khung nhìn thay vì một điều khiển cụ thể (xóa liên kết segue cũ, sau đó kéo điều khiển từ chính bộ điều khiển khung nhìn sang bộ điều khiển khung nhìn đích). Sau đó tạo một IBActiontrong trình điều khiển chế độ xem của bạn và nối điều khiển với IBAction. Sau đó, bạn có thể thực hiện logic của mình (kiểm tra TextField trống) trong IBAction mà bạn vừa tạo và quyết định xem có performSegueWithIdentifierlập trình hay không .


Nếu segue là một bộ điều khiển popover, bạn không muốn nhấn nút thứ hai để tạo một bộ điều khiển popover khác; điều đúng đắn trong trường hợp này là loại bỏ popover. Câu trả lời của bạn cho phép hành vi thích hợp này. Nếu bạn nối trực tiếp từ nút trong bảng phân cảnh, dù sao tôi cũng không thấy hành vi phù hợp.
wcochran

1
Sau nhiều giờ nản lòng khi cố gắng để có nhiều popovers dựa trên segue chơi cùng nhau, tôi đã từ bỏ và thoát khỏi các chuỗi popover có lợi cho giải pháp này. Nó thực sự sử dụng ít mã hơn.
mpemburn

Điều này sẽ không đánh bại mục đích của việc có sự khác biệt?
Cristik

Thực tế để liên kết ViewContoder với ViewContoder đã giải quyết vấn đề của tôi. Cảm ơn bạn! Đây là giải pháp tốt nhất
Tiến sĩ TJ

19

Swift 3 : func ShouldPerformSegue (định danh withIdentifier: String, sender: Any?) -> Bool

Trả về giá trị true nếu segue nên được thực hiện hoặc false nếu nó bị bỏ qua.

Ví dụ :

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

Ngoài ra, đó là hành vi hơi xấu khi cung cấp một nút mà người dùng không nên nhấn. Bạn có thể để segue có dây như chân đế, nhưng bắt đầu với nút bị tắt. Sau đó kết nối "chỉnh sửa" của UITextField với một sự kiện trên ala điều khiển chế độ xem

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

"Ngoài ra, đó là hành vi hơi xấu khi cung cấp một nút mà người dùng không nên nhấn". Tôi sẽ không đồng ý với điều này - điều này đúng một phần nhưng thực sự phụ thuộc vào bối cảnh. Đó cũng là hành vi xấu khi không hướng dẫn người dùng - ví dụ để họ có thể nhấn vào một nút và hệ thống giải thích những gì cần phải làm trước tiên. Với một nút bị vô hiệu hóa hoặc vô hình, người dùng sẽ bị mất hoặc gọi hỗ trợ ...
csmith

11

Nó dễ dàng trong nhanh chóng.

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

    return true
}

3
Huh? Bạn có thể giải thích thêm về câu trả lời này? Câu trả lời chỉ có mã không hữu ích cho người đọc thêm ...
Cristik

9

Như Áp-ra-ham đã nói, kiểm tra hợp lệ hay không trong chức năng sau.

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

Và, performSegueWithIdentifier:sender:được gọi bởi lập trình có thể bị chặn bằng cách ghi đè phương thức sau. Theo mặc định, nó không kiểm tra hợp lệ hay không -shouldPerformSegueWithIdentifier:sender:, chúng ta có thể thực hiện thủ công.

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

phần này có [super performSegueWithIdentifier:identifier sender:sender];thực sự đúng không?
Ben Wheeler

@BenWheeler Bạn có thể thử nó. Nếu bạn ghi đè performSegueWithIdentifier:sender:phương thức và không gọi superphương thức đó.
AechoLiu

5

Nên thực hiện phân đoạn cho đăng ký đăng nhập

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

Tương tự như câu trả lời của Kaolin là để phần tiếp theo được nối với điều khiển nhưng xác nhận điều khiển dựa trên các điều kiện trong chế độ xem. Nếu bạn đang kích hoạt tính tương tác của ô bảng thì bạn cũng cần đặt thuộc tính userInteractionEnables cũng như vô hiệu hóa nội dung trong ô.

Chẳng hạn, tôi đã có một biểu mẫu trong chế độ xem bảng được nhóm. Một trong các ô dẫn đến một bảng xem khác hoạt động như một bộ chọn. Bất cứ khi nào một điều khiển được thay đổi trong giao diện chính, tôi gọi phương thức này

-(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 Trả lời:

Sau đây là triển khai Swift 4 để hủy 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

Một cách khác là ghi đè phương thức của bảngView bằng willSelectRowAt và trả về nil nếu bạn không muốn hiển thị segue. showDetails()- là một số bool. Trong hầu hết các trường hợp nên được thực hiện trong mô hình dữ liệu được biểu diễn trong ô với indexPath.

 func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if showDetails() {
                return indexPath            
        }
        return nil
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.