在视图控制器之间移动和传递数据的正确方法

Posted

技术标签:

【中文标题】在视图控制器之间移动和传递数据的正确方法【英文标题】:Correct way to move between view controllers and pass data 【发布时间】:2012-08-05 15:40:51 【问题描述】:

我的根控制器是一个带有静态单元格和导航控制器的 TableViewController。 根据选择的单元格,我想推送另一个 ViewController(带有嵌入式 TableView),它将相应地更改其配置(是否启用按钮,来自 db 表或 NSArray 的单元格数据等)。 选择一些“Controller A”单元格将调用“Controller B”或“Controller C”并传递一些数据。

我正在尝试的代码如下所示:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
   switch (indexPath.row) 
        case 0:
        
            [self performSegueWithIdentifier:@"segueToType" sender:self];
        
            break;
        case 1:
        
            [self performSegueWithIdentifier:@"segueToType" sender:self];
        

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender


if ([[segue identifier] isEqualToString:@"segueToType"])

    NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
    NSInteger rowNumber = selectedIndexPath.row;
    switch (rowNumber) 
        case 0:
        
            TypeSelectController *selCon = [segue destinationViewController];
            selCon.myPet = self.myPet;
            selCon.sel = @"tipo";
            selCon.delegate = self;
        
            break;
        case 1:
        
            TypeSelectController *selCon = [segue destinationViewController];
            selCon.myPet = self.myPet;
            selCon.sel = @"razza";
            selCon.delegate = self;
        
            break;
        default:
            break;
    

 

在这种情况下,例如,我只调用“控制器 B”,但根据“控制器 A”的选定单元格,它以不同的方式配置自身(取决于“selCon.sel”字符串)。

在“控制器B”中,我有这样的代码:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

if ([sel isEqualToString:@"tipo"]) 
    NSString *itemToPassBack = [tipi objectAtIndex:indexPath.row];
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack ofType:@"tipo"];
    [self.navigationController popViewControllerAnimated:YES];
 else if ([sel isEqualToString:@"razza"]) 
    NSString *itemToPassBack = [razze objectAtIndex:indexPath.row];
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack ofType:@"razza"];
    [self.navigationController popViewControllerAnimated:YES];


同样,根据字符串“sel”的值,我将不同的数据返回到“控制器 A”。

当我选择控制器 A 中的第一个单元格时,我正确地获得了控制器 B 场景。然后我选择一个单元格并返回正确数据的控制器 A。 现在,如果我尝试选择控制器 A 中的第二个单元格并选择控制器 B 中的一个单元格,导航不会按我的意愿响应,我会得到控制器 C 场景,然后是控制器 A 和以下错误:

嵌套推送动画会导致导航栏损坏

在意外状态下完成导航转换。导航栏子视图树可能已损坏。

开始/结束外观转换的不平衡调用。

我做错了什么?

【问题讨论】:

我不确定这里是否有足够的代码来帮助你。你看起来没问题,除了你在用这条线做什么: [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack ofType:@"razza"];我不明白在 addItemViewController 委托调用中将 self 传回。您是否将其注入到您的视图控制器或导航流中? 这是使用委托方法将数据传回控制器 A 所需的部分代码。我使用了此链接中显示的代码:***.com/questions/5210535/… 该示例在理论上很好,但在您使用故事板和转场时不合适。您需要如何处理“要传回的项目”? - 特别是在使用协议和委托时,您通常设置一个属性或将一个属性传递给一个方法回原始 vc。我给你写一个答案。 @Aleph72 在回答您上面的评论时,子控制器 B 不一定需要将自己传递回父控制器 A。我可以想象一些您可能需要的情况,但我一般不会。 @Aleph72 几个问题: 1. 我假设您正在使用 push segues。 2.控制器B和控制器C都是TypeSelectController的实例吗? 3.你能告诉我们你的A控制器的addItemViewController代码吗? 4. 我假设您的 B 和 C 控制器本身没有嵌入到导航控制器中。 【参考方案1】:

好的,再试一次。

让我们使用头发颜色的例子。您在控制器 a 中有一个表格,其中有一个用于头发颜色的静态单元格。假设您显示所选颜色的文本值。 cell.textLabel = self.hairColor。如果它是空白的,用户可以触摸单元格并打开一个新的控制器来选择颜色。

在您选择的新视图控制器中,您设置了一个属性来告诉它查找“hairColor”。因此它从名为 listOfHairChoices 的数据库中构建了一个列表。

用户选择了第三行,因此您可以像这样使用委托引用在父级中设置属性。 self.delegate.hairColor = [self.listOfHairChoices objectAtIndex:indexPath.row]; 然后弹出关闭子 vc 并弹回父级。 [self.navigationController popViewCon..etc]

然后回到父级,在 -(void)viewDidAppear 中,您只需像这样重新加载您的 tableview:[self.tableView reloadData]

现在,您的原始 tableview 应该显示在子视图控制器中选择的单元格的值。

这是一种直截了当的方法,并且在每个步骤中都需要逻辑来确定您正在使用哪些属性以及需要设置哪些属性 - 但可以与您现有的框架一起使用。

另一种方法是为您呈现的数据创建一个模型类,然后将模型传递给子 VC - 让他们更新模型并将其传回,然后只需在每次更新后从更新的模型中重新加载您的表过渡。您可能不熟悉这种方法,但它可以简化事情并减少您现在为 tableview 的每一行所拥有的所有条件逻辑。

希望此建议对您的项目有所帮助。

【讨论】:

我已经有一个模型类,所以我认为我做的事情超出了我应该做的。今晚晚些时候,我会尝试根据你所说的做出一些改变,并让你知道。现在非常感谢你们俩的帮助。 好的,我正在尝试模型类方法。我已经将我的模型类(myPet)的一个实例发送给子 VC。然后在那个 VC 中,我可以根据选择的单元格简单地更新模型。现在我对如何将更新的模型发送回父 VC 有一些疑问。 好的,我做到了。我不需要做任何奇怪的事情来传回更新的模型。我只是在子 VC 中更新它,然后弹出它。在父 VC 中重新加载 tableview 显示更新的数据。我在导航变得疯狂时遇到的问题似乎已经解决了。我将这个答案标记为正确的答案,即使你们俩都帮了我很多。再次感谢。【参考方案2】:

在故事板示例中使用协议和委托的快速示例。只是在这里打亮点,因为你已经大部分时间了。

控制器 a(父)。有一个 NSString 属性,称为 category 和一个称为 partNumber。有一个表视图。选择行来启动一个segue。 [self performSegueWithIdentifier:@"segueToType" sender:self];

(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

if ([[segue 标识符] isEqualToString:@"segueToType"]) ControllerB *b = segue.destinationViewController; b.category = self.category; b.代表=自我;

视图控制器 B

@协议 -(void)doSomethingWithPickedPartNumber:(NSString *)partNumber; @结束

//用传入的类别的零件号设置tableview。

//选择了行,所以我们有了想要发回的零件号。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

NSString *pn = [self.listOfParts objectAtIndex:indexPath.row];

 //options - set the variable in the parent or call a method in the parent
 self.delegate.partNumber = pn;
 //if you set the variable in the parent - you can check for it in the -(void)viewWillAppear  method and reconfigure the parent view if needed

 //or do this
 [self.delegate doSomethingWithPickedPartNumber:pn];

 //this is actually calling a method in the parent.  remember you don't have a view you can see yet, but its in memory and you can modify things so when the child pops back it will look how you want it.  Also note, you can name the protocol methods what ever you want, as long as they match up from the parent  to the protocol in the child.


 //then return to parent
 [self.navigationController popViewControllerAnimated:YES];

【讨论】:

以上是关于在视图控制器之间移动和传递数据的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

在视图控制器之间传递数据而没有 segues

如何在 Swift 中的视图之间传递数据

在视图控制器之间传递数据 - iOS Xcode - 啥是好方法?

Swift - 在视图控制器之间传递数据

prepareForSegue 是在视图控制器之间传递值的正确方法吗

如何在视图控制器之间传递数据而不在 ios 中使用 segue?