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

时间:2016-11-17 16:33:16

标签: swift switch-statement optional-binding

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

示例:

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是由分割视图控制器触发的,那么目标是导航控制器,你真正想做的是打开导航控制器的顶视图控制器的类? / p>

我想做这样的事情:

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中使用像这样的开关语句的多部分可选绑定?

5 个答案:

答案 0 :(得分:1)

我认为有一种方法可以通过switchcase执行此操作,但您可以使用if和{case更接近您要查找的内容{1}}(更新:正如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语句实际上不会被编译器验证为处理所有情况(因为您需要创建默认情况),因此使用{{1}没有额外的好处而不只是switch

答案 1 :(得分:1)

您可能会发现此扩展有用...

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也可以用作非导航控制器的地方使用。

答案 2 :(得分:1)

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

它涉及在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
    }
}

答案 3 :(得分:0)

可选绑定并不适合像您尝试这样的交换机。

我理解使用开关的愿望而不是简单的if if if if,但它在概念上与开关的意图有所不同。

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

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"
    }
}

答案 4 :(得分:0)

在我看来,您应该使用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()
}