通过storyboard和xcode正确创建手动segue

时间:2012-09-20 18:03:57

标签: ios xcode storyboard

我对xcode很新,我正在尝试开发一个基本上是嵌入式tableview的示例应用程序,它有很多级别。我有一个plist存储每个tableview的单元格。如果细胞没有孩子,那么我希望能够在按下细胞后进入一个详细视图。最终,我希望能够根据数据类型转到不同的详细视图。为此,我从故事板创建了一个详细视图,将我的视图控制器拖到我的详细视图中以创建手动“推”segue,并将segue标记为“segue1”。

修改:源代码here

Building Manual Segue

接下来,我填充了我认为必须执行的功能,即调用[self performSegueWithIdentifier:@"segue1" sender:myString];,其中myString是我选择的单元格的标题。

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

    }

}

最后,我打电话给segue做准备,寻找标记为segue1的segue。

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

我认为这样可行,但出于某种原因,每当代码到达[self performSegueWithIdentifier:@"segue1" sender:myString];时,它就会出现错误

*****由于未捕获的异常'NSInvalidArgumentException'终止应用程序,原因:'Receiver()没有带标识符'segue1'的segue' *第一次抛出调用堆栈: (0x14b4022 0xeb4cd6 0xdf61b 0x3590 0xa85c5 0xa87fa 0x93d85d 0x1488936 0x14883d7 0x13eb790 0x13ead84 0x13eac9b 0x139d7d8 0x139d88a 0x17626 0x23ed 0x2355 0x1) 终止称为抛出异常(lldb)

我无法弄清楚为什么当它已经在故事板和代码中定义时它告诉我它找不到segue1。

2 个答案:

答案 0 :(得分:23)

实际上有几个问题:

首先,在您上传给我们的项目中,segue 带有“segue1”标识符:

no identifier

如果您尚未填写该标识符,则应填写该标识符。

第二次,当您从表格视图推送到表格视图时,您正在调用initWithNibName来创建视图控制器。你真的想使用instantiateViewControllerWithIdentifier

因此,该行说:

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

应该说:

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

第三次,您的prepareForSegue是:

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

应简化以消除对visibleViewController的引用,例如:

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

第四,您的DrillDownDetailController.m使用此方法:

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

这里有很多问题,但你不应该在这里更新label.text(因为它可能尚未创建!)。您应该完全消除这种自定义setter方法(只需让Xcode为您合成标准setter),然后将viewDidLoad更改为如下所示:

- (void)viewDidLoad
{
    [super viewDidLoad];

    label.text = self.itemName;
}

确保在viewDidLoad之前不更新UI对象!

我没有完成整个程序,但是通过这四个修正,我可以点击“SubItem1”,然后点击“汽车”,然后点击“宝马”,我看到一个详细的屏幕上写着“宝马”。我认为你的plist在其他项目上存在一些问题(例如,鞋子条目是字符串,而不是字典条目,你得到一个错误......我猜你还没有完全填写你的plist),但上面的修正纠正更重要的编码问题。

答案 1 :(得分:2)

主要问题在这里,您正在创建一个空白的DrillDownViewController,而不是重复使用故事板中的那个,请参阅以下代码中的注释

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

}


    }