简体   繁体   中英

Swift 3: How to assign variable of different option types without multiple if let statements?

first off, I apologize if the title is ambiguous. Please feel free to change it to fit the problem description.

I'll try to describe my problem as best as I can, but first here's a snippet of code:

if let vc = currentVC as? FirstViewController
{
    vc.doSameMethod()
}
else if let vc = currentVC as? SecondViewController
{
    vc.doSameMethod()
}
else if let vc = currentVC as? ThirdViewController
{
    vc.doSameMethod()   
}

Basically, I'm using the if let statement to check for optional nil, then unwrapping it and assigning it.

I have the doSameMethod() function in all 3 ViewControllers perform almost identical tasks with the same function name.

My question is, how can I do something like this to avoid repetitious code:

if let vc = (currentVC as? FirstViewController || current as? SecondViewController || currentVC as? ThirdViewController)
{
    vc.doSameMethod()
}

Obviously, the code above does not compile, but I'd like to know if there's an easier and cleaner way to unwrap an optional by checking it against multiple types?

Thanks!

You can do it with a protocol. Define a protocol with the common methods between all 3 view controllers and make them conform to it:

protocol MyViewControllerProtocol {
    func doSameMethod()
}

class FirstViewController: UIController, MyViewControllerProtocol {
    // your code
}

class SecondViewController: UIController, MyViewControllerProtocol {
    // your code
}

class ThirdViewController: UIController, MyViewControllerProtocol {
    // your code
}

Usage:

if let vc = currentVC as? MyViewControllerProtocol {
    vc.doSameMethod()
}

If you have multiple view controllers that all implement the same method then a protocol is a good way to go, as outlined in @CodeDifferent's answer.

If each view controller has a different interface and you need to do different things depending on the view controller that's passed to you, you can use a variant of the switch statement with a case let newConstant as <type>:

Here is an example code snippet used in a prepareForSegue method, where you might be handling a segue to one of several different classes of view controller:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  switch segue.destinationViewController {
    case let fooViewController as FooViewController:
      fooViewController.delegate = self

    case let barViewController as BarViewController:
      barViewController.data = data

    default:
      break
  }
}

In your case it would read

switch currentVC {
  case let firstVC as FirstViewController:
      //code for FirstViewController
  case let secondVC as SecondViewController:
      //code for SecondViewController
  case let thirdVC as ThirdViewController:
      //code for ThirdViewController
}

A case is chosen if currentVC is an object of the class specified. Further, inside that case statement there is a local constant defined of the correct type, just like in an if let.


Edit - full example copied from "Matching based on class - great for prepareForSegue" from the now-defunct StackOverflow Docs:

You can also make a switch statement switch based on the class of the thing you're switching on.

An example where this is useful is in prepareForSegue . I used to switch based on the segue identifier, but that's fragile. if you change your storyboard later and rename the segue identifier, it breaks your code. Or, if you use segues to multiple instances same view controller class (but different storyboard scenes) then you can't use the segue identifier to figure out the class of the destination.

Swift switch statements to the rescue.

Use Swift case let var as Class syntax, like this:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  switch segue.destinationViewController {
    case let fooViewController as FooViewController:
      fooViewController.delegate = self

    case let barViewController as BarViewController:
      barViewController.data = data

    default:
      break
  }
}

In Swift 3 the sytax has changed slightly:

override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {       
  switch segue.destinationViewController {
    case let fooViewController as FooViewController:
      fooViewController.delegate = self

    case let barViewController as BarViewController:
      barViewController.data = data

    default:
      break
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM