Does Cocoa Connection Binding to Nstoolbaritem Prevent Deinitializing

Does Cocoa connection binding to NSToolbarItem prevent deinitializing?

As Willeke suggested, toolbarItem.view was the path to success. Have to say that the hierarchy of objects is not always clear to me, but .view seemed the only possibility as I looked at the toolbarItem hooks overnight, and Willeke's suggestion confirmed it. The Apple documentation on binding does mention it as our responsibility to unbind some objects, while it unbinds others. Here's the code I put in my NSDocument subclass for unbinding everything, and the view controller is now deinitializing.

func windowWillClose(notification: NSNotification) {
let wcs = self.windowControllers
if wcs.count == 0 { return }
let wc = wcs[0]
let toolbar = wc.window?.toolbar
if toolbar != nil {
let items = toolbar!.items
for item in items {
let v = item.view
if v != nil {
// print(v?.className)
let objects = v!.exposedBindings
for object in objects {
// print(object + " " + item.label + " " + v!.className)
v!.unbind(object)
}
}
}
}
}

This was one of the most confusing concepts I've run into--so many moving parts--and thanks to Willeke & stevesliva for the dialog that wound its way to a solution.

From NSObject(NSKeyValueBindingCreation):

All standard bindings on AppKit objects (views, cells, table columns,
controllers) unbind their bindings automatically when they are
deallocated, but if you create key-value bindings for other kind of
objects, you need to make sure that you remove those bindings before
deallocation (observed objects have weak references to their
observers, so controllers/model objects might continue referencing and
messaging the objects that were bound to them).

How to set NSSlider value in a NSToolbar - Swift Cocoa + Storyboard

Thanks for the proposed solutions. It certainly put me in the wrigth direction.

Here is what I did. In the WindowController , I set a toolbar with 1) button «previous», 2) button «next» and 3) a slider «slider».

Those are linked to the proper IBOutler and IBaction in the WindowController.
The viewController have a textLabel «myLabel»

The 2 buttons and the slider change the slider_ptr value in the ViewControler and is sent to myLabel. Also, the slider.label change according to the slider_pointer and the slider_max values. Here is the code for the windowController:


import Cocoa

class WindowController: NSWindowController {


@IBOutlet weak var slider: NSSlider!
@IBOutlet weak var sliderTB: NSToolbarItem!

var viewController: ViewController {
get {
return self.window!.contentViewController! as! ViewController
}
}

override func windowDidLoad() {
super.windowDidLoad()

setSlider() // set initial value based on ViewController

}




@IBAction func previous(_ sender: Any) {

viewController.previous (WindowController())
setSlider()

}

@IBAction func next(_ sender: Any) {
//viewController.updateMyLabelText(newText: "Prev Button clicked! ")
viewController.next (WindowController()) //send to VC function previous
// let pt = viewController.slider_ptr + 1
//let sMax = viewController.slider_max
setSlider()
//sliderTB.label = String(pt) + " de " + String(sMax)
}


@IBAction func sliderDidChange(_ sender: Any) {



viewController.sliderDidSlide (WindowController(), pointer: Int(slider.doubleValue))
setSlider()

// viewController.sliderDidSlide(PosiWC(), sValue: myslider.doubleValue)

}


func setSlider() {

/* myslider.minValue = 1
myslider.maxValue = Double(max)
myslider.integerValue = pointer*/

//print ("WCP58:" , myslider.integerValue )

let pt = viewController.slider_ptr
let sMax = viewController.slider_max
//slider (max : pt, pointer: sMax)
sliderTB.label = String(pt) + " de " + String(sMax)

slider.minValue = 1
slider.maxValue = Double(sMax)
slider.integerValue = pt

}
}

and for the Viewcontroller :

 class ViewController: NSViewController {


var slider_ptr = 1 // slider position
var slider_max: Int = 0 //

@IBOutlet weak var myLabel: NSTextField!

override func viewDidLoad() {
super.viewDidLoad()

slider_max = 250
myLabel.stringValue = String(slider_ptr)


}

override var representedObject: Any? {
didSet {
}
}

func previous(_ sender: Any) {
if slider_ptr > 1 {
slider_ptr -= 1
}
else { NSSound.beep()}

myLabel.stringValue = String(slider_ptr)

}


func next(_ sender: Any) {

if slider_ptr < slider_max {
slider_ptr += 1

}
else { NSSound.beep()}
myLabel.stringValue = String(slider_ptr)

}

func sliderDidSlide(_ sender: Any, pointer : Int) {
print (pointer)
slider_ptr = pointer
myLabel.stringValue = String(slider_ptr)

}
}

Align NSToolbarItems with NSSplitView columns

When using macOS 11 or newer, you can insert NSTrackingSeparatorToolbarItem items to the toolbar, which will split up your toolbar in sections, aligned with the dividers of a NSSplitView object.

This example adds the new separator items to a toolbar that already contains the rest of the buttons, configured in Interface Builder or in code. The target splitview concerns a standard configuration of 3 splitviews, including a sidebar panel.

class WindowController: NSWindowController, NSToolbarDelegate {

let mainPanelSeparatorIdentifier = NSToolbarItem.Identifier(rawValue: "MainPanel")

override func windowDidLoad() {
super.windowDidLoad()

self.window?.toolbar?.delegate = self

// Calling the inserts async gives more time to bind with the split viewer, and prevents crashes
DispatchQueue.main.async {

// The .sidebarTrackingSeparator is a built-in tracking separator which always aligns with the sidebar splitview
self.window?.toolbar?.insertItem(withItemIdentifier: .sidebarTrackingSeparator, at: 0)

// Example of a custom mainPanelSeparatorIdentifier
// Index at '3' means that there are 3 toolbar items at the left side
// of this separator, including the first tracking separator
self.window?.toolbar?.insertItem(withItemIdentifier: mainPanelSeparatorIdentifier at: 3)
}
}

func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {

if let splitView = (self.contentViewController as? NSSplitViewController)?.splitView {

// You must implement this for custom separator identifiers, to connect the separator with a split view divider
if itemIdentifier == mainPanelSeparatorIdentifier {
return NSTrackingSeparatorToolbarItem(identifier: itemIdentifier, splitView: splitView, dividerIndex: 1)
}
}
return nil
}
}

If you want to add an extra separator, for example for an Inspector panel, simply insert an additional toolbar item identifier to the toolbar, and assign an extra NSTrackingSeparatorToolbarItem to another divider in the itemForItemIdentifier delegate function.



Related Topics



Leave a reply



Submit