Swift: Switch Between Nsviewcontroller Inside Container View/Nsview

Swift: Switch between NSViewController inside Container View / NSView

Just note that in order for this to work you have to add storyboard identifiers to your view controllers, which can by going to your storyboard then selecting the Identity Inspector in the right hand pane and then entering the Storyboard ID in the Identity subcategory.

Then this implementation of ViewController would achieve what you are looking for.

import Cocoa

class ViewController: NSViewController {

// link to the NSView Container
@IBOutlet weak var container : NSView!

var vc1 : ViewController1!
var vc2 : ViewController2!

var vc1Active : Bool = false

override func viewDidLoad() {
super.viewDidLoad()

// Make sure to set your storyboard identiefiers on ViewController1 and ViewController2
vc1 = NSStoryboard(name: "name", bundle: nil).instantiateController(withIdentifier: "ViewController1") as! ViewController1
vc2 = NSStoryboard(name: "name", bundle: nil).instantiateController(withIdentifier: "ViewController2") as! ViewController2

self.addChild(vc1)
self.addChild(vc2)
vc1.view.frame = self.container.bounds
self.container.addSubview(vc1.view)
vc1Active = true

}

// You can link this action to both buttons
@IBAction func switchViews(sender: NSButton) {

for sView in self.container.subviews {
sView.removeFromSuperview()
}

if vc1Active == true {

vc1Active = false
vc2.view.frame = self.container.bounds
self.container.addSubview(vc2.view)

} else {

vc1Active = true
vc1.view.frame = self.container.bounds
self.container.addSubview(vc1.view)
}

}
}

NSViewController displayed inside a containerView doesn't align itself to the bounds of the Container in Cocoa App Swift [Mac OS ]

The problem is that when you are using constraints, they resize with changes in layout. But in vc2, you have given it a constant size at the start. Which is why even if you resize the window, it does not get resized. Try programmatically assigning constraints to vc2.view to attach it to its superview (cv). This will make sure that vc2.view will resize with cv.

Make sure to assign constraints after cv.addSubview(vc2.view).

Check out this library which will remove a lot of boiler plate code while creating constraints.

Container View in Cocoa, Swap Contents at Runtime and Preserve Size

It turns out all I needed to do is add this line before setting up my constraints:

    newView.translatesAutoresizingMaskIntoConstraints = false

It isn't clear to me why I should need this, given that the child view I want to add belongs to a view controller created from a storyboard (and those are supposed to already not translate autoresizing mask into constraints...?); this code should only be needed for views created programmatically...

macOS - Swift 4.0 Container View does not show NSView

I found sample code SourceView but it is rather complicated and in Objective-C. Here's my translation of the relevant part to Swift (I'm not very familiar with Swift).

class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {

var tableViewArray = ["Thema 1", "Thema 2", "Thema 3", "Thema 4", "Thema 5"]
var detailViewController: NSViewController?

@IBOutlet weak var container: NSView!

@IBAction func tableViewClickAction(_ tableView: NSTableView) {
if tableView.clickedRow >= 0 {
print("\(tableViewArray[tableView.clickedRow]), clicked")

// remove the detail view if there is one
if let oldViewController = detailViewController {
oldViewController.view.removeFromSuperview()
oldViewController.removeFromParentViewController()
detailViewController = nil
}

// determine the identifier of the new detail view, this is the Storyboard ID of the detail scene
var sceneIdentifier: NSStoryboard.SceneIdentifier?
switch tableView.clickedRow {
case 0:
sceneIdentifier = NSStoryboard.SceneIdentifier(rawValue: "Thema1")
case 1:
sceneIdentifier = NSStoryboard.SceneIdentifier(rawValue: "Thema2")
case 2:
sceneIdentifier = NSStoryboard.SceneIdentifier(rawValue: "Thema3")
case 3:
sceneIdentifier = NSStoryboard.SceneIdentifier(rawValue: "Thema4")
case 4:
sceneIdentifier = NSStoryboard.SceneIdentifier(rawValue: "Thema5")
default:
sceneIdentifier = nil
}

if sceneIdentifier != nil {
// load the new detail controller and view from the storyboard
detailViewController = self.storyboard?.instantiateController(withIdentifier:sceneIdentifier!) as? NSViewController
if let newViewController = detailViewController {
newViewController.view.translatesAutoresizingMaskIntoConstraints = false
// add controller and view
self.addChildViewController(newViewController)
container.addSubview(newViewController.view)
// add constraints
let views = ["targetView": newViewController.view]
let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|[targetView]|",
options: [], metrics: nil, views: views)
let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|[targetView]|",
options: [], metrics: nil, views: views)
NSLayoutConstraint.activate(horizontalConstraints)
NSLayoutConstraint.activate(verticalConstraints)
}
}
}
else {
print("Clicked outside the cells")
}
}

}

How to pass value from NSViewController to custom NSView of NSPopover?

Ok, there's some architecture mistakes. You don't need delegate method and protocol at all. All you just need is well defined setter method:

I. Place your PlasmidMapView into NSViewController-subclass. This view controller must be set as contentViewController-property of your NSPopover-control. Don't forget to set it the way you need in viewDidLoad-method or another.

class PlasmidMapController : NSViewController {
weak var mapView: PlacmidMapView!
}

II. In your PlacmidMapView don't forget to call needsDisplay-method on dna did set:

class PlasmidMapView: NSView {
//...
var dnaForMap = String() {
didSet {
needsDisplay()
}
//...
}

III. Set dna-string whenever you need from your MainController-class.

@IBAction func actionPopoverPlasmidMap(sender: AnyObject) {
popoverPlasmidMap.showRelativeToRect(sender.bounds,
ofView: sender as! NSView, preferredEdge: NSRectEdge.MinY)

let dna = inputDnaFromUser.string
if let controller = popoverPlasmidMap.contentViewController as? PlasmidMapController {
controller.mapView.dna = dna
} else {
fatalError("Invalid popover content view controller")
}
}

NSViewController reference cycle with storyboards

Another answer.

It seems, only the segues defined on Storyboard can perform view controller deallocation.
So, Here is a very ugly but working workaround.

screenshot

class DismissSegue: NSStoryboardSegue {

var nextViewControllerIdentifier:String?

override func perform() {
let src = self.sourceController as NSViewController
let windowController = src.view.window!.windowController() as TopLevelWindowController

src.view.removeFromSuperview()
src.removeFromParentViewController()

if let identifier = nextViewControllerIdentifier {
windowController.setNewViewController(identifier)
}
}
}

class TopLevelWindowController: NSWindowController {

var containerView: NSView!
var containerViewController: ContainerViewController! {
didSet {
setNewViewController("FirstView")
}
}

func setNewViewController(identifier: String) {
// Create and set up the new view controller and view.
let viewController = storyboard!.instantiateControllerWithIdentifier(identifier) as NSViewController
let view = viewController.view
view.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(viewController.view)
containerViewController.addChildViewController(viewController)
containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: nil, metrics: nil, views: ["view": view]))
containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: nil, metrics: nil, views: ["view": view]))
}
}

class ContainerViewController: NSViewController {

@IBOutlet var containerView: NSView!

override func viewDidAppear() {
super.viewDidAppear()
if let window = view.window {
if let topLevelWindowController = window.windowController() as? TopLevelWindowController {
topLevelWindowController.containerView = containerView
topLevelWindowController.containerViewController = self
}
}
}

}

class FirstViewController: NSViewController {

required init?(coder: NSCoder) {
super.init(coder: coder)
let pointerAddress = NSString(format: "%p", unsafeBitCast(self, Int.self))
NSLog("First VC init at \(pointerAddress)")
}

deinit {
let pointerAddress = NSString(format: "%p", unsafeBitCast(self, Int.self))
NSLog("First VC de-init at \(pointerAddress)")
}

override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject?) {
if let segue = segue as? DismissSegue {
segue.nextViewControllerIdentifier = "SecondView"
}
}
}

class SecondViewController: NSViewController {

required init?(coder: NSCoder) {
super.init(coder: coder)
let pointerAddress = NSString(format: "%p", unsafeBitCast(self, Int.self))
NSLog("Second VC init at \(pointerAddress)")
}

deinit {
let pointerAddress = NSString(format: "%p", unsafeBitCast(self, Int.self))
NSLog("Second VC de-init at \(pointerAddress)")
}

override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject?) {
if let segue = segue as? DismissSegue {
segue.nextViewControllerIdentifier = "FirstView"
}
}
}

Procedure for modifying your Storyboard:

  1. Disconnect @IBAction from buttons.
  2. Create "DUMMY" view controller scene with normal NSViewController.
  3. Connect custom segues from buttons to "DUMMY".
  4. Configure these segue as shown.

Please let me know if this solution doesn't meet your demand.

Does an NSFilePromiseProviderDelegate need to be also an NSView or NSViewController?

Does an NSFilePromiseProviderDelegate need to be also an NSView or NSViewController?

No.

delegate is a local variable and is released at the end of pasteboardWriter(forImageCanvas:). Without a delegate the file promise provider doesn't work. Solution: keep a strong reference to the delegate.

class ImageCanvasController: NSViewController, ImageCanvasDelegate, NSToolbarDelegate {

let delegate = CustomFilePromiseProviderDelegate()

...

func pasteboardWriter(forImageCanvas imageCanvas: ImageCanvas) -> NSPasteboardWriting {
let provider = NSFilePromiseProvider(fileType: kUTTypeJPEG as String, delegate: delegate)
provider.userInfo = imageCanvas.snapshotItem
return provider
}
}


Related Topics



Leave a reply



Submit