Shanmugaraja G
Shanmugaraja G

Reputation: 2778

Cocoa - Present NSViewController programmatically

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.

Upvotes: 0

Views: 9590

Answers (4)

Kelvin Fok
Kelvin Fok

Reputation: 661

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

Upvotes: 2

Marek H
Marek H

Reputation: 5566

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

Upvotes: 1

Corxit Sun
Corxit Sun

Reputation: 741

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

Upvotes: -1

DairySeeker
DairySeeker

Reputation: 326

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 :)

Upvotes: 3

Related Questions