13

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

选项 1:使用委托。

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

选项 2:使用 unwind segue

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

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

4

2 回答 2

11

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

耦合

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

灵活性

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

可维护性

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

获胜者,冠军

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

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

于 2013-11-03T18:24:31.963 回答
7

我对故事板非常怀疑,但我决定深入研究并在一个新项目中使用它们。我很惊讶两个视图控制器之间可以轻松地进行通信。当您执行 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 是干净和独特地耦合这两个类的好方法。我强烈推荐它。

于 2013-10-24T03:27:07.223 回答