Search code examples
iosswiftsegueuistoryboardsegueunwind-segue

Unwind of custom segue not triggered


Problem

My problem is that unwind process is not triggered even though back button is clicked.

My code

I have a class 1stViewController that uses a custom segue to 2ndViewController. The custom segue is the following:

override func perform() {
    let sourceVC = self.source as! 1stViewController
    let destinationVC = self.destination as! 2ndViewController
    sourceVC.addChildViewController(destinationVC)
    sourceVC.view.addSubview(destinationVC.view)
}

In my 2ndViewController I have added a button that triggers an unwinding segue back to 1stViewController. The unwinding segue is connected through the identifier of the unwind segue triggering exit in the 2ndViewController. Thus, I have created an IBAction that the unwind segue exit is connected to:

@IBAction func unwindToFeed(segue: UIStoryboardSegue) {}

I have set the identifier of the unwind segue equal to the one used in the action function of the button

func handleActionBackButton(){
    print("here")
    self.performSegue(withIdentifier: "unwindId", sender: self)
}

"Here" is printed when button is clicked, but it seems like performSegue is not.

I guessed that I needed a custom unwindSegue class as well, so I created unwindSegueEventToFeed:

class unwindSegueEventToFeed: UIStoryboardSegue {

override func perform() {
    let sourceVC = self.source as! 2ndViewController
    let destinationVC = self.destination as! 1stViewController

    let sourceView = sourceVC.view as UIView!
    let destView = destinationVC.view as UIView!

    let window = UIApplication.shared.keyWindow
    window?.insertSubview(destView!, aboveSubview: sourceView!)
    sourceVC.dismiss(animated: false, completion: nil)
}

} ...which is called in the 2ndViewController:

override func segueForUnwinding(to toViewController: UIViewController, from fromViewController: UIViewController, identifier: String?) -> UIStoryboardSegue? {
    return unwindSegueEventToFeed(identifier: identifier, source: fromViewController, destination: toViewController)
}

Any clues on what I have to do different to trigger the unwinding process?


Solution

  • I got your example to work like this. ViewController is the first view controller; ViewController2 is the second view controller:

    class ViewController: UIViewController {
        @IBAction func unwindToFeed(_ segue: UIStoryboardSegue) {
            let child = self.childViewControllers[0]
            child.willMove(toParentViewController: nil)
            child.view.removeFromSuperview()
            child.removeFromParentViewController()
        }
    }
    
    class MySegue: UIStoryboardSegue {
        override func perform() {
            let sourceVC = self.source as! ViewController
            let destinationVC = self.destination as! ViewController2
            sourceVC.addChildViewController(destinationVC)
            sourceVC.view.addSubview(destinationVC.view)
            destinationVC.view.frame = sourceVC.view.bounds
            destinationVC.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
            destinationVC.didMove(toParentViewController: sourceVC)
        }
    }
    
    class ViewController2 : UIViewController {
    }
    

    Note that unwindToFeed is (and must be) in the first view controller. It is meaningless to put it in the second view controller, as the second view controller is the source, not the destination, of the unwind.

    Now I'd like to make some comments about the problem and my answer:

    • This is an iOS 10 example. segueForUnwinding will never be called in iOS 10; it was abandoned after iOS 8 and was subsequently deprecated.

    • The iOS 9/10 equivalent of segueForUnwinding is unwind(for:towardsViewController:). I did try implementing it, but I found that it was called in ViewController2, not in ViewController. I regard that as a bug in iOS 10, and am reporting it to Apple.