ios 通过故事板和 xcode 正确创建手动转场

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12518271/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 20:16:41  来源:igfitidea点击:

Creating manual segue correctly through storyboard and xcode

iosxcodestoryboard

提问by foboi1122

I'm quite new to xcode, and I am trying to develop a sample app that is basically an embedded tableview which has many levels. I have a plist which stores the cells of each tableview. If the cells do not have children, then I want to be able to go to one detailed view once the cell is pressed. Eventually I want to be able to go to different detailed views depending on data type. To do this, I created a detailed view from storyboard, dragged my view controller to my detailed view to create a manual "Push" segue, and labeled the segue "segue1".

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

Edit:Source code here

编辑:源代码在这里

Building Manual Segue

构建手动转场

Next I populate what I assumed to be the necessary functions for this to work, which is to call [self performSegueWithIdentifier:@"segue1" sender:myString];where myString is the title of the cell I selected.

接下来,我填充我假设的必要函数,即调用[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;

    }

}

Finally, I called prepare for segue which looks for the segue labeled segue1.

最后,我调用prepare for 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];
    }
}

I thought this would work, but for some reason, whenever the code reaches [self performSegueWithIdentifier:@"segue1" sender:myString];, it breaks with the error

我认为这会起作用,但出于某种原因,每当代码到达时[self performSegueWithIdentifier:@"segue1" sender:myString];,它就会因错误而中断

***** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver () has no segue with identifier 'segue1'' * First throw call stack: (0x14b4022 0xeb4cd6 0xdf61b 0x3590 0xa85c5 0xa87fa 0x93d85d 0x1488936 0x14883d7 0x13eb790 0x13ead84 0x13eac9b 0x139d7d8 0x139d88a 0x17626 0x23ed 0x2355 0x1) terminate called throwing an exception(lldb)

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

I can't figure out why it's telling me it can't find segue1 when it's already defined in storyboard and the code.

我不明白为什么当它已经在故事板和代码中定义时它告诉我它找不到 segue1。

回答by Rob

A couple of problems, actually:

几个问题,实际上:

First, in that project you uploaded for us, the segue does notbear the "segue1" identifier:

首先,在您上传的我们的项目中,SEGUE并不会承担“segue1”标识:

no identifier

没有标识符

You should fill in that identifier if you haven't already.

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

Second, as you're pushing from table view to table view, you're calling initWithNibNameto create a view controller. You really want to use instantiateViewControllerWithIdentifier.

其次,当您从表视图推送到表视图时,您正在调用initWithNibName以创建视图控制器。你真的很想用instantiateViewControllerWithIdentifier.

Thus, the line that says:

因此,该行说:

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

should say:

应该说:

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

Third, your prepareForSeguewas:

第三,你prepareForSegue是:

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

And it should be simplified to eliminate reference to visibleViewController, e.g.:

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

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

Fourth, your DrillDownDetailController.mhas this method:

第四,你DrillDownDetailController.m有这个方法:

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

There are a bunch of problems here, but you simply should not be updating the label.texthere (because it might not yet be created!). You should just eliminate this custom setter method completely (just let Xcode synthesize the standard setter for you) and just change your viewDidLoadto read as follows:

这里有很多问题,但你根本不应该更新label.text这里(因为它可能还没有被创建!)。您应该完全消除这个自定义的 setter 方法(只需让 Xcode 为您合成标准的 setter),然后将您viewDidLoad的内容更改为如下:

- (void)viewDidLoad
{
    [super viewDidLoad];

    label.text = self.itemName;
}

Make sure you don't update UI objects until viewDidLoad!

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

I haven't gone through the whole program, but with those four corrections I can tap on "SubItem1", and then "Cars", and then "BMW" and I see a detail screen that says "BMW". I think there are some problems with your plist on other items (e.g. the shoes entries are strings, and not dictionary entries and you get an error ... I presume you just haven't filled in your plist completely), but the above fixes correct the more significant coding issues.

我还没有完成整个程序,但是通过这四个更正,我可以点击“SubItem1”,然后点击“Cars”,然后点击“BMW”,我会看到一个显示“BMW”的详细信息屏幕。我认为您的 plist 在其他项目上存在一些问题(例如,鞋子条目是字符串,而不是字典条目,并且您会收到错误……我想您只是没有完全填写您的 plist),但以上修复了纠正更重要的编码问题。

回答by mkral

THE MAIN ISSUE IS HERE, you were creating a blank DrillDownViewController, not reusing the one from storyboards, see comments in code below

主要问题在这里,您正在创建一个空白的 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;

}


    }