web-dev-qa-db-fra.com

Créer un enchaînement manuel correctement via storyboard et xcode

Je suis tout à fait nouveau sur xcode, et j'essaie de développer un exemple d'application qui est essentiellement une table intégrée qui a plusieurs niveaux. J'ai une liste qui stocke les cellules de chaque table. Si les cellules n'ont pas d'enfants, je veux pouvoir accéder à une vue détaillée une fois la cellule appuyée. Finalement, je veux pouvoir accéder à différentes vues détaillées en fonction du type de données. Pour ce faire, j'ai créé une vue détaillée à partir du storyboard, j'ai fait glisser mon contrôleur de vue vers ma vue détaillée pour créer un enchaînement "Push" manuel et j'ai étiqueté l'enchaînement "segue1".

Modifier: Code source ici

Building Manual Segue

Ensuite, je remplis ce que j'ai supposé être les fonctions nécessaires pour que cela fonctionne, qui est d'appeler [self performSegueWithIdentifier:@"segue1" sender:myString]; où myString est le titre de la cellule que j'ai sélectionnée.

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

    }

}

Enfin, j'ai appelé prepare for segue qui recherche la séquence étiquetée 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];
    }
}

Je pensais que cela fonctionnerait, mais pour une raison quelconque, chaque fois que le code atteint [self performSegueWithIdentifier:@"segue1" sender:myString];, ça rompt avec l'erreur

***** Arrêt de l'application en raison d'une exception non interceptée 'NSInvalidArgumentException', raison: 'Receiver () n'a pas de séquence avec l'identificateur' segue1 '' * Première pile d'appels de lancement: (0x14b4022 0xeb4cd6 0xdf61b 0x3590 0xa85c5 0xa87fa 0x93d85d 0x1488936 0x14883d7 0x13eb790 0x13ead84 0x13eac9b 0x139d7d8 0x139d88a 0x17626 0x23ed 0x2355 0x1) se termine appelé en lançant une exception (lldb)

Je ne peux pas comprendre pourquoi il me dit qu'il ne peut pas trouver segue1 alors qu'il est déjà défini dans le storyboard et le code.

18
foboi1122

Quelques problèmes, en fait:

First, dans ce projet que vous avez téléchargé pour nous, le segue ne porte pas l'identifiant "segue1":

no identifier

Vous devez remplir cet identifiant si vous ne l'avez pas déjà fait.

Second, lorsque vous passez d'une vue de table à une vue de table, vous appelez initWithNibName pour créer un contrôleur de vue. Vous voulez vraiment utiliser instantiateViewControllerWithIdentifier.

Ainsi, la ligne qui dit:

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

devrait dire:

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

Troisième, votre prepareForSegue était:

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

Et il devrait être simplifié pour éliminer la référence à visibleViewController, par exemple:

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

Quatrième, votre DrillDownDetailController.m a cette méthode:

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

Il y a un tas de problèmes ici, mais vous ne devriez tout simplement pas mettre à jour le label.text ici (car il n'est peut-être pas encore créé!). Vous devez simplement éliminer complètement cette méthode de définition personnalisée (laissez simplement Xcode synthétiser la définition standard pour vous) et changez simplement votre viewDidLoad pour lire comme suit:

- (void)viewDidLoad
{
    [super viewDidLoad];

    label.text = self.itemName;
}

Assurez-vous de ne pas mettre à jour les objets de l'interface utilisateur avant le viewDidLoad!

Je n'ai pas parcouru tout le programme, mais avec ces quatre corrections, je peux appuyer sur "SubItem1", puis "Cars", puis "BMW" et je vois un écran de détail qui dit "BMW". Je pense qu'il y a des problèmes avec votre plist sur d'autres éléments (par exemple, les entrées de chaussures sont des chaînes et non des entrées de dictionnaire et vous obtenez une erreur ... Je suppose que vous n'avez tout simplement pas complètement rempli votre plist), mais les correctifs ci-dessus corriger les problèmes de codage les plus importants.

23
Rob

LE PRINCIPAL PROBLÈME IS ICI, vous créiez un DrillDownViewController vierge, sans réutiliser celui des story-boards, voir les commentaires dans le code ci-dessous

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

}


    }
2
mkral