如何在准备中的 switch 语句中使用可选绑定(segue :)

Posted

技术标签:

【中文标题】如何在准备中的 switch 语句中使用可选绑定(segue :)【英文标题】:How to use optional binding in switch statement in prepare(segue:) 【发布时间】:2016-11-17 16:33:16 【问题描述】:

在 swift 中,您可以使用prepare(segue:) 中 switch 语句的一个很酷的功能来根据目标视图控制器的 type 创建案例:

例子:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
    switch segue.destination 

    case let detailViewController as DetailViewController:
      detailViewController.title = "DetailViewController"
    
    case let otherViewController as OtherViewController:
      otherViewController.title = "OtherViewController"
    

但是,如果 segue 是由拆分视图控制器触发的,那么目的地是导航控制器,而您真正想要做的是打开导航控制器的顶部视图控制器的类?

我想做这样的事情:

case let nav as UINavigationController,
     let detailViewController = nav.topViewController as? DetailViewController:
    //case code goes here

我在if let 可选绑定中使用了相同的构造。

这行不通。相反,我必须像这样做一个相当痛苦的构造:

case let nav as UINavigationController
  where nav.topViewController is DetailViewController:
  guard let detailViewController = nav.topViewController as? DetailViewController
    else 
      break
  
  detailViewController.title = "DetailViewController"

这行得通,但它似乎不必要地冗长,并且模糊了意图。在 Swift 3 中这样的 switch 语句的情况下,有没有办法使用多部分可选绑定?

【问题讨论】:

从 switch 语句中删除 where 子句意味着这种情况会将 any 捕获到导航控制器,这不是我需要的。我正在寻找一种在 switch 语句的情况下进行多部分可选绑定的方法。 哎呀,是的,你完全正确。 通常的做法是开启segue的identifier 【参考方案1】:

我认为 switchcase 没有办法做到这一点,但你可以用 ifcase 做一些更接近你正在寻找的事情(更新:正如 Hamish 指出的那样出来,case 在这种情况下甚至不需要)或者只是普通的if let

override func prepare(for segue: UIStoryboardSegue, sender: Any?) 

    if let nav = segue.destination as? UINavigationController, 
        let detailViewController = nav.topViewController as? DetailViewController 
        detailViewController.title = "DetailViewController"
    

    if let otherViewController? = segue.destination as? OtherViewController 
        otherViewController.title = "OtherViewController"
    

由于此示例中的 switch 语句实际上不会被编译器验证为处理所有情况(因为您需要创建默认情况),因此使用 switch 而不仅仅是if let

【讨论】:

if case let nav? 在 switch 语句之外是什么意思? 或者只是if let nav = segue.destination as? UINavigationController, ... @DuncanC if case 在 if 语句中执行模式匹配,nav? 的模式是 optional pattern。当结果为非零时,它与表达式segue.destination as? UINavigationController 匹配(它被解包并绑定到nav)。尽管这在功能上与可选绑定完全相同。【参考方案2】:

你可能会发现这个扩展很有用……

extension UIStoryboardSegue 
    var destinationNavTopViewController: UIViewController? 
        return (destination as? UINavigationController)?.topViewController ?? destination
    

那么你可以简单地……

override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
    switch segue.destinationNavTopViewController 
    case let detailViewController as? DetailViewController:
        // case code goes here

不是?? destination 确保返回值是非可选的,并且还允许它在destination 也可能是非导航控制器的地方工作。

【讨论】:

不错的解决方案。不过,请参阅我的自我回答。我认为它更灵活,并且更明确地说明了正在发生的事情。【参考方案3】:

我为这个问题找到了一个不错的解决方案。

它涉及在 switch 语句之前进行一些设置,然后在 switch 语句中使用一个元组。看起来是这样的:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
    let dest = segue.destination
    let navTopVC = (dest as? UINavigationController)?.topViewController
    switch (dest, navTopVC) 

    case (_, let top as VC1):
        top.vc1Text = "Segue message for VC1"

    case (_, let top as VC2):
        top.vc2Text = "Segue message for VC2"

    case (let dest as VC3, nil):
        dest.vc3Text = "Segue message for VC3"

    default:
        break
    

【讨论】:

【参考方案4】:

可选绑定并不真正适合您尝试执行的开关。

我理解使用 switch 的愿望,而不是简单的 if 和 if else,但它在概念上与 switch 的意图有点不同。

无论如何,这是我在大多数情况下使用的两个选项

func prepare(for segue: UIStoryboardSegue, sender: Any?) 

    switch segue.destination 
    case is DetailViewController:
        segue.destination.title = "DetailViewController"
    case is OtherViewController:
        segue.destination.title = "OtherViewController"
    default:
        break
    

func prepare(for segue: UIStoryboardSegue, sender: Any?) 

    if let controller = seuge.destination as? DetailViewController 
        controller.title = "DetailViewController"
    
    else if let controller = seuge.destination as? OtherViewController 
        controllercontroller.title = "OtherViewController"
    

【讨论】:

感谢您的意见,但我不同意。 case let xx as class 非常适合这种情况。它甚至是量身定做的。看看我的问题中的第一个代码块。该语法允许您打开目标视图控制器的类并同时进行可选绑定。 这里的复杂因素是,如果目的地是导航控制器,我真的想根据目的地视图控制器的顶视图控制器的类进行切换。 但是您并没有根据变量的值进行切换,您只是在创建新变量。等同于 if-let 我可以想象它会在某个时候被添加,但这违背了当前的 switch 语句理念 不,它与if let 不同。它实际上是根据目标视图控制器的 CLASS 进行切换,并在这种情况下创建一个适当类型的局部变量,一次完成。真的。【参考方案5】:

在我看来,您应该使用 segue 标识符来控制此开关中的流程。但是要回答您的问题,这应该对您有用。

switch (segue.destination as? UINavigationController)?.topViewController ?? segue.destination 
    ...

问题是,根据语法,案例项目列表中的每个项目只有一个模式(在你的情况下它是绑定的)。由于您在输入中只有一个项目,但想使用两种模式,您要么想规范化输入(在这种情况下,如上所示),要么扩展项目列表(在这种情况下不合适,但我展示了一个下面的例子)。

switch ((segue.destination as? UINavigationController)?.topViewController, segue.destination) 
case (let tvc, let vc) where (tvc ?? vc) is DetailViewController:
    // TODO: If you await your DetailViewController in navigation or without.
    break
case (let tvc as DetailViewController, _):
    // TODO: If you await your DetailViewController in navigation but other vc is not in a navigation vc.
default:
    fatalError()

【讨论】:

以上是关于如何在准备中的 switch 语句中使用可选绑定(segue :)的主要内容,如果未能解决你的问题,请参考以下文章

c语言switch语句中default是啥意思?

switch case 语句

switch-case 执行顺序

Go常见语句

switch语句中的扫描程序问题

Java switch case和数组