Crear segue manual correctamente a través de storyboard y xcode

Soy bastante nuevo en xcode y estoy intentando desarrollar una aplicación de ejemplo que sea básicamente una vista de tabla incrustada que tenga muchos niveles. Tengo una pluma que almacena las celdas de cada vista de tabla. Si las celdas no tienen hijos, entonces quiero poder ir a una vista detallada una vez que se presiona la celda. Eventualmente quiero poder ir a diferentes vistas detalladas dependiendo del tipo de datos. Para hacer esto, creé una vista detallada desde el guión gráfico, arrastré mi controller de vista a mi vista detallada para crear un segue manual "Push", y etiqueté el segue "segue1".

Editar: código fuente aquí

Manual de construcción Segue

Luego relleno lo que supuse que son las funciones necesarias para que esto funcione, que es llamar a [self performSegueWithIdentifier:@"segue1" sender:myString]; donde myString es el título de la celda que seleccioné.

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { //Check the dictionary to see what cell was clicked NSDictionary *dict = [self.tableDataSource objectAtIndex:indexPath.row]; NSString *myString = [dict objectForKey:@"Title"]; NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row]; NSArray *children = [dictionary objectForKey:@"Children"]; //If there is no children, go to the detailed view if([children count] == 0) { [self performSegueWithIdentifier:@"segue1" sender:myString]; } else{ //Prepare to tableview. DrillDownViewController *rvController = [[DrillDownViewController alloc] initWithNibName:nil bundle:[NSBundle mainBundle]]; //Increment the Current View rvController.CurrentLevel += 1; //Set the title; rvController.CurrentTitle = [dictionary objectForKey:@"Title"]; //Push the new table view on the stack [self.navigationController pushViewController:rvController animated:YES]; rvController.tableDataSource = children; } } 

Finalmente, llamé a preparar para segue que busca la segue labelda segue1.

 -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([[segue identifier] isEqualToString:@"segue1"]) { DrillDownDetailController *dvController = [[segue destinationViewController] visibleViewController]; //DrillDownDetailController *dvController = [[DrillDownDetailController alloc] initWithNibName:nil bundle:[NSBundle mainBundle]]; [dvController setItemName:(NSString *)sender]; [self.navigationController pushViewController:dvController animated:YES]; } } 

Pensé que esto funcionaría, pero por alguna razón, cada vez que el código alcanza [self performSegueWithIdentifier:@"segue1" sender:myString]; , rompe con el error

***** Aplicación de finalización debido a una exception no detectada 'NSInvalidArgumentException', motivo: 'Receptor () no tiene segue con identificador' segue1 '' * Primera stack de llamadas de lanzamiento: (0x14b4022 0xeb4cd6 0xdf61b 0x3590 0xa85c5 0xa87fa 0x93d85d 0x1488936 0x14883d7 0x13eb790 0x13ead84 0x13eac9b 0x139d7d8 0x139d88a 0x17626 0x23ed 0x2355 0x1) terminate called throwing an exception (lldb)

No puedo entender por qué me está diciendo que no puede encontrar segue1 cuando ya está definido en el storyboard y el código.

Un par de problemas, en realidad:

Primero , en ese proyecto que cargó para nosotros, segue no lleva el identificador "segue1":

sin identificador

Debe completar ese identificador si aún no lo ha hecho.

En segundo lugar , a medida que avanza desde la vista de tabla a la vista de tabla, está llamando a initWithNibName para crear un controller de vista. Realmente quieres usar instantiateViewControllerWithIdentifier .

Por lo tanto, la línea que dice:

 DrillDownViewController *rvController = [[DrillDownViewController alloc] initWithNibName:nil bundle:[NSBundle mainBundle]]; 

debería decir:

 DrillDownViewController *rvController = [self.storyboard instantiateViewControllerWithIdentifier:@"TableView"]; 

Tercero , su prepareForSegue fue:

 -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([[segue identifier] isEqualToString:@"segue1"]) { dvController = [[segue destinationViewController] visibleViewController]; [dvController setItemName:self->nameToSend]; } } 

Y debería simplificarse para eliminar la reference a visibleViewController , por ejemplo:

 -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([[segue identifier] isEqualToString:@"segue1"]) { dvController = segue.destinationViewController; dvController.itemName = nameToSend; } } 

En cuarto lugar , su DrillDownDetailController.m tiene este método:

 -(void) setItemName:(NSString *)itemName { if(!itemName) { itemName = [[NSString alloc] initWithString:itemName]; } label.text = itemName; } 

Hay un montón de problemas aquí, pero simplemente no debería actualizar la label.text aquí (¡porque aún no se puede crear!). Simplemente elimine completamente este método de configuration personalizado (simplemente deje que Xcode sintetice el viewDidLoad estándar para usted) y simplemente cambie su viewDidLoad para que lea como sigue:

 - (void)viewDidLoad { [super viewDidLoad]; label.text = self.itemName; } 

¡Asegúrate de no actualizar los objects de la interfaz de usuario hasta viewDidLoad !

No he revisado todo el progtwig, pero con esas cuatro correcciones puedo tocar "SubItem1", luego "Cars" y luego "BMW" y veo una pantalla de detalles que dice "BMW". Creo que hay algunos problemas con tu plist en otros artículos (por ejemplo, las inputs de zapatos son cadenas y no inputs de dictionary y obtienes un error … supongo que simplemente no has completado tu plist por completo), pero las correcciones anteriores corregir los problemas de encoding más importantes.

EL PROBLEMA PRINCIPAL ESTA AQUÍ, estaba creando un DrillDownViewController en blanco, no reutilizando el de los storyboards, vea los comentarios en el siguiente código

  -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([[segue identifier] isEqualToString:@"segue1"]) { [segue.destinationViewController setItemName:(NSString*)sender]; } } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { //Check the dictionary to see what cell was clicked NSDictionary *dict = [self.tableDataSource objectAtIndex:indexPath.row]; NSString *titleName = [dict objectForKey:@"Title"]; NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row]; NSArray *children = [dictionary objectForKey:@"Children"]; if([children count] == 0) { [self performSegueWithIdentifier:@"segue1" sender:titleName]; } else{ //Prepare to tableview. //THE MAIN ISSUE IS HERE, you were creating a blank DrillDownViewController, not reusing the one from storyboards so it did not have a segue at all defined. instead do this: UIStoryboard* sb = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil]; DrillDownViewController *rvController = [sb instantiateViewControllerWithIdentifier:@"DDVC"]; //Increment the Current View rvController.CurrentLevel += 1; //Set the title; rvController.CurrentTitle = [dictionary objectForKey:@"Title"]; //Push the new table view on the stack [self.navigationController pushViewController:rvController animated:YES]; rvController.tableDataSource = children; } }