user14492 user14492 - 2 months ago 18
Objective-C Question

Transitioning between view controller, OS X

I am trying to write a single window timer application, where when the user presses the start button I want it to show another view controller with countdown etc. I'm also using story board in Xcode, where I have got a segue which connects the start button and the second view controller. However, there are only three different styles i.e. modal, sheet, and pop-over. I want to replace the first view controller the second one in the window. I cannot find a way to do that. I tried using a custom style for the segue, and in that use presentViewController: animator: method but I cannot figure out what to send as the argument for the animator:.

What is the simplest/proper way to transition from one view controller to the other in one window and vice versa?

Also in the storyboard when I select a view controller it shows an attribute called "Presentation" which can be multiple and single, what do those represent?

Answer

I think the simplest way is that swapping contentViewController of NSWindow.

// in NSViewController's subclass
@IBAction func someAction(sender: AnyObject) {
    let nextViewController = ... // instantiate from storyboard or elsewhere
    view.window?.contentViewController = nextViewController
}

This is option #1.

If you want to use segue, create custom one and set it to segue class with identifier in IB.

class ReplaceSegue: NSStoryboardSegue {
    override func perform() {
        if let fromViewController = sourceController as? NSViewController {
            if let toViewController = destinationController as? NSViewController {
                // no animation.
                fromViewController.view.window?.contentViewController = toViewController
            }
        }
    }
}

This is option #2.

Last option is using presentViewController:animator: of NSViewController. The code below is custom NSViewControllerPresentationAnimator for dissolve animation.

class ReplacePresentationAnimator: NSObject, NSViewControllerPresentationAnimator {
    func animatePresentationOfViewController(viewController: NSViewController, fromViewController: NSViewController) {
        if let window = fromViewController.view.window {
            NSAnimationContext.runAnimationGroup({ (context) -> Void in
                fromViewController.view.animator().alphaValue = 0
            }, completionHandler: { () -> Void in
                viewController.view.alphaValue = 0
                window.contentViewController = viewController
                viewController.view.animator().alphaValue = 1.0
            })
        }
    }

    func animateDismissalOfViewController(viewController: NSViewController, fromViewController: NSViewController) {
        if let window = viewController.view.window {
            NSAnimationContext.runAnimationGroup({ (context) -> Void in
                viewController.view.animator().alphaValue = 0
                }, completionHandler: { () -> Void in
                    fromViewController.view.alphaValue = 0
                    window.contentViewController = fromViewController
                    fromViewController.view.animator().alphaValue = 1.0
            })
        }        
    }
}

Then present VC like this.

@IBAction func replaceAction(sender: AnyObject) {
    let nextViewController = ... // instantiate from storyboard or elsewhere
    presentViewController(nextViewController, animator: ReplacePresentationAnimator())
}

For dismissal, call presentingViewController's dismissViewController: in the presented VC.

@IBAction func dismissAction(sender: AnyObject) {
    presentingViewController?.dismissViewController(self)    
}

Hope this help.