0

Generally, We can able to display next view controller from first view controller by having different kind of NSStoryboardSeque like Present, Show, Sheet etc., But, How we can achieve the same programmatically?.

Comparing with UIViewController, presenting a view controller modally by presentViewController:animated:. Is there any same kind of approach for NSViewController?

Thanks in advance.

Shanmugaraja G
  • 2,778
  • 4
  • 31
  • 47

4 Answers4

3

The two different presentation types I use are:

func presentViewControllerAsModalWindow(_ viewController: NSViewController)
func presentViewControllerAsSheet(_ viewController: NSViewController)

After doing some more research another way to do using:

func presentViewController(_ viewController: NSViewController, animator: NSViewControllerPresentationAnimator)

And eating a custom presentation animator. Here you have the freedom to do what you like :)

p13n
  • 859
  • 8
  • 31
DairySeeker
  • 326
  • 3
  • 7
  • What did you do with NSViewControllerPresentationAnimator? How do you call or create it? – Silve2611 Dec 12 '16 at 11:03
  • 2
    I couldn't really find much documentation on creating an NSViewControllerPresentationAnimator, but here's a good answer that contains an example. https://stackoverflow.com/questions/28454291/transitioning-between-view-controller-os-x – Ryan H Apr 27 '18 at 18:04
1

If you have a view controller (presenting) than it's as simple as following function are provided:

open func presentAsSheet(_ viewController: NSViewController)
open func presentAsSheet(_ viewController: NSViewController)
open func present(_ viewController: NSViewController, asPopoverRelativeTo positioningRect: NSRect, of positioningView: NSView, preferredEdge: NSRectEdge, behavior: NSPopover.Behavior)

If you need to present a view controller in a new window (NOT MODAL) you need to create own NSWindow, NSWindowController

let gridView = NSGridView(views: [
    [NSTextField(labelWithString: "label1"),NSTextField(labelWithString: "label2")],
    [NSTextField(labelWithString: "label3"),NSTextField(labelWithString: "label4")]
])
let viewController = NSViewController()
viewController.view = gridView
let window = NSWindow(contentViewController: viewController)
window.center()
let windowController = NSWindowController(window: window)
windowController.showWindow(nil)

EXPLANATION:

Storyboards are using seques to perform some magic. The show seque is simply calling action "perform:" on object NSStoryboardShowSegueTemplate ([NSApp sendAction:to:from]). This seque will create NSWindowController and NSWindow (private method windowWithContentViewController:) for you and on top it will layoutSubviews/resize and center the window. Magic bonus is self retaining the window so you don't care about memory management.

Example of programatic calling (using Storyboards to instantiate windowController with viewController)

import Cocoa
import Contacts

class ShorteningHistoryWindowController : NSWindowController, Storyboarded {
    static var defaultStoryboardName = "ShorteningHistory"
}

struct ShorteningHistory {
    static let shared = ShorteningHistory()
    private var windowController : NSWindowController
    private init() {
        windowController = ShorteningHistoryWindowController.instantiate()
    }
    public func showHistory() {
        windowController.showWindow(self)
    }
}


extension Storyboarded where Self: NSWindowController {
    static var defaultStoryboardName: NSStoryboard.Name { return String(describing: self) }
    static var defaultIdentifer: NSStoryboard.SceneIdentifier {
        let fullName = NSStringFromClass(self)
        let className = fullName.components(separatedBy: ".")[1]
        return className
    }

    static func instantiate() -> Self {
        let storyboard = NSStoryboard(name: defaultStoryboardName, bundle: Bundle.main)
        guard let vc = storyboard.instantiateController(withIdentifier: defaultIdentifer) as? Self else {
            fatalError("Could not instantiate initial storyboard with name: \(defaultIdentifer)")
        }

        return vc
    }
}

PS: Don't forget to set Storyboard Identifiers in Storyboard

Marek H
  • 5,173
  • 3
  • 31
  • 42
1

In case someone is looking for the solution in 2022,

extension NSViewController {

   func presentInNewWindow(viewController: NSViewController) {
      let window = NSWindow(contentViewController: viewController)

      var rect = window.contentRect(forFrameRect: window.frame)
      // Set your frame width here
      rect.size = .init(width: 1000, height: 600)
      let frame = window.frameRect(forContentRect: rect)
      window.setFrame(frame, display: true, animate: true)

      window.makeKeyAndOrderFront(self)
      let windowVC = NSWindowController(window: window)
      windowVC.showWindow(self)
  }
}
Kelvin Fok
  • 621
  • 7
  • 9
0

1.Create a NSViewController instance with StoryBoard Identifier

let theTESTVCor =  self.storyboard?.instantiateController(withIdentifier: "TESTVCor") as! NSViewController

2.Present In Via the current NSViewController

theNSViewController.presentViewControllerAsModalWindow(theTESTVCor)

⚠️ DO NOT FORGET to set the Identifier of the NSViewController in Storyboard

Corxit Sun
  • 618
  • 6
  • 8