将数据传递给父场景的委托与展开 Segue

Posted

技术标签:

【中文标题】将数据传递给父场景的委托与展开 Segue【英文标题】:Delegate vs Unwind Segue for Passing Data to Parent Scene 【发布时间】:2013-10-24 02:52:13 【问题描述】:

ios 6 开始,可以使用展开转场来向上导航场景层次结构。我正在尝试决定将数据传递给父视图控制器的更清洁/更好/首选/更可维护的方法。有一些问题可以从技术角度解决这个问题(例如,“如果我有一个放松,我还需要一个代表吗”),但我找不到很多可以解决利弊的问题。

选项 1:使用委托。

通过将父视图控制器作为遵守协议的委托传入来完成。 子调用协议方法返回数据。 如果 Parent 需要数据验证,则需要返回 value/dict 以允许 child 处理错误。 开销:父级中的协议定义和一种方法(用于数据验证和接收)。

选项 2:使用展开转场

通过调用 child 的 unwind segue 来完成。 儿童通过将按钮或情节提要本身拖到Exit 并命名该segue 以使其可以与performSegueWithIdentifier:sender 一起在其场景中添加一个segue 父级实现returnFromSegueName(链接到该segue的用户命名方法)以从子级获取数据。 虽然只能通过同时实现canPerformUnwindSegueAction:fromViewController:withSender来实现数据验证 数据验证失败将需要 Child 上的另一个属性,因为此方法只接受 BOOL 作为返回值。 开销:两种方法,一个额外的属性,以及 Storyboard 恶作剧。

总体而言,代表们感觉要走的路更干净,但也可能过时了。我偏向那个方向是错的吗?

【问题讨论】:

【参考方案1】:

我现在意识到,除了说这两种方法都没有错误之外,这并不是一个真正可以回答的问题——它们都有其优点和缺点。在解决了这两个问题一周并对该主题进行了更多阅读之后,我至少可以量化为什么您可能希望使用 unwind segue 或委托在视图控制器之间工作。

耦合

两个模型大致相等(松散)耦合。在底层,一个 unwind segue 只是一个代理,iOS 已经为你完成了连接它的工作。对于委托,父母知道并遵守子协议。对于展开 segue,父节点必须连接到情节提要上的子节点以进行展开,并且需要知道子节点的属性以提取返回数据。但是,如果您是代理的新手,并且只想从子视图中获取一些数据,那么展开 segue 可能不如使用代理协议那么令人生畏。

灵活性

只有当子父交互的唯一目的是返回数据时,展开转场才是一个不错的选择。似乎没有办法取消正在进行的展开转场。因此,如果父级必须进行任何数据验证,或者如果子级需要与父级进行多次交互,那么唯一的方法是拥有一个可以将多个方法回调给父级的委托。

可维护性

如果返回的数据的类型或其他方面发生变化,更新展开转场会更容易,因为您只需更新展开转场中的代码以查看新属性。对于协议/委托方法,您必须更新子协议和父协议的实现。然而,unwind segue 的简单性是以您可能很容易错过需要更新的父视图控制器中的位置为代价的,因为您没有编译器检查您的合同(协议)。

赢家

没有。您采用哪种方式取决于您的数据需求、协议的舒适程度(乍一看,它们看起来比实际情况更令人生畏)、应用程序的复杂性以及长期维护需求。

出于我的目的,我最终使用了委托,因为在某些情况下,我的孩子不得不多次回电给父母。然而,在一些我有很多数据要传回的情况下,我采用了我从 unwind segue 中学到的东西,并简单地使用了子元素中的属性,父元素可以从中提取所需的信息。我还用它作为父母向孩子提供错误信息的便捷途径。为了与编程合作伙伴保持一致,我不会在程序中将 unwind segues 与代表混合和匹配,但如果你愿意,你没有理由不能这样做。

【讨论】:

另一个我还没有完全解决的考虑:看起来展开 segues 允许您展开多次推送以一举返回多个级别。我认为这也可以通过其他方式实现,但使用 unwind segues 看起来要容易得多。 我同意通过多个视图控制器展开是一个巨大的优势【参考方案2】:

我对故事板非常怀疑,但我决定深入研究并将它们用于一个新项目。我很惊讶两个视图控制器之间可以轻松地进行通信。当您执行 performSegueWithIdentifier 时,您将获得新 ViewController 的句柄。您可以在新的 viewController 中非常干净和漂亮地设置任何您想要的公开属性。

这是一个例子:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 
    if ([[segue identifier] isEqualToString:@"showDetail"]) 
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        Student *student = [self.students objectAtIndex:indexPath.row + [self rowAdjuster]];
        [[segue destinationViewController] setStudent:student];
     

非常漂亮和整洁。您无需跟踪或维护任何特殊协议。

然后回来(我有一个 IBAction 连接到我的详细视图中的一个按钮)您可以再次获得对您要返回的 viewController 的干净引用,并对该 viewController 采取行动。

- (IBAction)returnWithStudent:(UIStoryboardSegue *)segue 
    UIViewController *vc = [segue sourceViewController];
    if ([vc isKindOfClass:[ AddStudentViewController class]]) 
        AddStudentViewController *addViewController = (AddStudentViewController *)vc;
        if (addViewController.student != nil) 
            if ([addViewController hasTakenPhoto]) 
                [PhotoHelpers saveImageForStudent:addViewController.student];
            
            [StudentController updateStudent:addViewController.student];
        
    

segue 逻辑控制也很好。可以在 shouldPerformSegue 中执行逻辑检查,非常方便。

我见过很多垃圾代码,它们使用“将某些东西发回给调用者”的协议,这些协议在耦合类方面确实很差。它进行了三向排列--viewController1 -> 协议-> viewController2,而segues 对viewController1->viewController2 进行了很好的排列。

segue 是一种干净且独特地耦合两个类的好方法。我强烈推荐它。

【讨论】:

我很失望的呼叫转场。那里没有问题。这是回报。您正在获取returnWithStudent 中的数据。但是如果您需要进行数据验证……您不能在那里进行(无法取消)。而且您不能在shouldPerformSegue 中执行此操作,因为那是在子控制器中(我假设父级必须进行验证,因为子级不应该知道数据模型)。因此,除非我误解了某些内容,否则您可以使用 canPerformUnwindSegueAction:fromViewController:withSender 为什么要将您的数据验证与任何一个 viewController 结合起来?为什么父母比孩子更了解数据模型?您的数据验证应该是您的数据控制器或类似的非 ViewController 类的一部分。 同意 - 父级实际上用模型检查它。我正在抽象一步。无论哪种情况,问题都是如果您想发送数据、验证数据并在需要时传递结果/反馈,则必须设置双向流。正如我所说,我认为它可以通过放松来完成,但我担心它笨重,我认为这不是canPerformUnwindSegueAction:fromViewController:withSender 的预期目的。我同意你的看法,但使用 unwind 似乎不太耦合。

以上是关于将数据传递给父场景的委托与展开 Segue的主要内容,如果未能解决你的问题,请参考以下文章

在函数内将数据传递给 segue.destination 的问题

Swift segue 不会将数据传递给旧的 ViewController

Swift:将数据传递给 segue

在准备 segue 时,如何将数据传递给子 UIViewController?

从 tableviewcontroller 将数据传递给父 viewcontroller

使用 Storyboard Segue iOS 将数据传递给视图控制器