¿Previene segue en el método prepareForSegue?

249

¿Es posible cancelar un segue en el prepareForSegue:método?

Quiero realizar alguna verificación antes del segue, y si la condición no es verdadera (en este caso, si algo UITextFieldestá vacío), mostrar un mensaje de error en lugar de realizar el segue.

Shmidt
fuente

Respuestas:

485

Es posible en iOS 6 y versiones posteriores: debe implementar el método

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

En su controlador de vista. Haces tu validación allí, y si está bien, entonces return YES;si no lo está return NO;y no se llama a prepareForSegue.

Tenga en cuenta que este método no se llama automáticamente cuando se desencadena segues mediante programación. Si necesita realizar la verificación, debe llamar a shouldPerformSegueWithIdentifier para determinar si debe realizar una segue.

Abrahán
fuente
106
Para su información, si el segue se activa mediante programación llamando a [self performSegueWithIdentifier: @ "segueIdentifier" sender: nil]; shouldPerformSegueWithIdentifier nunca será llamado.
El tipo
3
@Thedude gracias por señalar esto. Estaba rastreando un problema y no estaba llegando a mi punto de ruptura. Para cualquier persona curiosa, solo necesita llamar a este método envuelto en una declaración if para obtener el mismo resultado.
jpittman
1
@jpittman, ¿podría explicar a qué se refiere envuelto en una declaración if?
Boda Taljo
77
@AubadaTaljo: (disculpas por formatear) if ([self shouldPerformSegueWithIdentifier:@"segueIdentifier" sender:nil]) { [self performSegueWithIdentifier:@"segueIdentifier" sender:nil]; }
TimMedcalf
Probé esto en iOS 11.3 SDK desde una secuencia de guiones gráficos y "shouldPerformSegueWithIdentifier" fue llamado automáticamente
Menno
52

Nota: la respuesta aceptada es el mejor enfoque si puede apuntar a iOS 6. Para apuntar a iOS 5, esta respuesta servirá.

No creo que sea posible cancelar una sesión prepareForSegue. Sugeriría mover su lógica al punto de que el performSeguemensaje se envía primero.

Si está utilizando Interface Builder para conectar un segue directamente a un control (por ejemplo, vincular un segue directamente a un UIButton), puede lograr esto con un poco de refactorización. Conecte el segue al controlador de vista en lugar de un control específico (elimine el enlace de segue anterior y luego arrastre el control desde el controlador de vista hasta el controlador de vista de destino). Luego cree un IBActionen su controlador de vista y conecte el control al IBAction. Luego puede hacer su lógica (verifique el campo de texto vacío) en la IBAction que acaba de crear, y decidir allí si programar o no performSegueWithIdentifier.

Mike Mertsock
fuente
Si el segue es a un controlador de popover, no desea que un segundo toque del botón cree otro controlador de popover; Lo correcto en este caso es descartar el popover. Su respuesta permite este comportamiento adecuado. Si lo conecta directamente desde el botón en el guión gráfico, no veo de todos modos para obtener el comportamiento adecuado.
wcochran
1
Después de varias horas frustrantes de tratar de hacer que varios popovers basados ​​en segue jueguen bien juntos, me di por vencido y me deshice de los secuestros popover a favor de esta solución. En realidad usa menos código.
mpemburn
¿No derrotaría esto el propósito de tener segues?
Cristik
El hecho de vincular ViewController a ViewController resolvió mi problema. ¡Gracias! Esta es la mejor solución
Dr. TJ
19

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

Valor de retorno verdadero si se debe realizar la segue o falso si se debe ignorar.

Ejemplo :

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
fuente
12

Alternativamente, es un mal comportamiento ofrecer un botón que un usuario no debe presionar. Puede dejar el segue conectado como soportes, pero comience con el botón deshabilitado. Luego, conecte la "ediciónChanged" de UITextField a un evento en el control de vista ala

- (IBAction)nameChanged:(id)sender {
    UITextField *text = (UITextField*)sender;
    [nextButton setEnabled:(text.text.length != 0)];
}
Kaolin Fire
fuente
"Alternativamente, es un comportamiento algo malo ofrecer un botón que un usuario no debe presionar". No estaría de acuerdo con esto: esto es parcialmente cierto, pero realmente depende del contexto. También es un mal comportamiento no guiar al usuario, por ejemplo, para que pueda tocar un botón y el sistema explique qué debe hacerse primero. Con un botón deshabilitado o invisible, los usuarios se perderán o llamarán al servicio de asistencia ...
csmith
11

Es fácil en el rápido.

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

    return true
}
Zumry Mohamed
fuente
3
¿Eh? ¿Puedes dar más detalles sobre esta respuesta? Las respuestas de solo código no son muy útiles para otros lectores ...
Cristik
9

Como dijo Abraham, marque válido o no en la siguiente función.

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

Y, la performSegueWithIdentifier:sender:llamada por programación puede bloquearse sobrescribiendo el siguiente método. Por defecto, no está comprobando la validez o no -shouldPerformSegueWithIdentifier:sender:, podemos hacerlo 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];
}
AechoLiu
fuente
¿Es esta parte [super performSegueWithIdentifier:identifier sender:sender];realmente cierta?
Ben Wheeler
@BenWheeler Puedes probarlo. Si anula el performSegueWithIdentifier:sender:método y no llama a su supermétodo.
AechoLiu
5

Debe realizar Segue para iniciar sesión Registrarse

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

Similar a la respuesta de Kaolin es dejar la secuencia conectada al control pero validar el control según las condiciones de la vista. Si está activando la interacción de la celda de la tabla, también debe establecer la propiedad userInteractionEnabled, así como deshabilitar las cosas en la celda.

Por ejemplo, tengo un formulario en una vista de tabla agrupada. Una de las celdas conduce a otra tableView que actúa como un selector. Cada vez que se cambia un control en la vista principal, llamo a este método

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

Respuesta rápida 4:

La siguiente es la implementación de Swift 4 para cancelar la 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
fuente
2

La otra forma es anular el método de tableView con willSelectRowAt y devolver nil si no desea mostrar el segue. showDetails()- Es un bool. En la mayoría de los casos debe implementarse en el modelo de datos que se representa en la celda con indexPath.

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