Kodeco Forums

Cocoa Bindings on macOS

Cocoa bindings make glue code a thing of the past. Discover how you can simplify your controller code in this Cocoa Bindings on macOS Tutorial!


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/921-cocoa-bindings-on-macos

Great article about cocoa binding. Been using it for a couple of years. you can also use binding in a more dynamic way duplicate-free views also using DryView: here it is and a sample project using it Reusable XIBs in cocoa binding (DRY Views) NSView, Part 2 ā€“ AMTourky

Hi Andy,

I tried binding a view to a property in Objective-C, just as you did in Swift. But it doesnā€™t work.

The only step of your tutorial that I cannot reproduce in Objective-C is the ā€œdynamicā€ variable keyword. Do you think that could be the reason why it does not work?

Thanks!

Hey @jmoukel,

I wish I had a bit more time to dive into your question, but hereā€™s a link to an older Cocoa Bindings tutorial I did a few years ago, in Objective-C. Hopefully it helps a bit (it is quite old at this point). https://www.raywenderlich.com/21752/how-to-use-cocoa-bindings-and-core-data-in-a-mac-app

This is a great tutorial. It has really helped in building my own app. Is there a way you can add how to sort the table by clicking the columns? It seems easy to do but every other site Iā€™ve found so far seems to ignore showing how to do it using Interface Builder. I mean I see each column has fields for ā€˜Sort Keyā€™ and ā€˜Selectorā€™ but not quite sure how to update this area and/or add methods to make sorting the columns work; no matter what I do when I click the column headers the sort doesnā€™t happen. Iā€™ve figured out how to do a sort when the table first loads, programmatically, by calling the .sorted(by:) method on the array but how do I control the sorting in by using the column headers?

@macandyp Andy,

Thanks for the tutorial. It was really helpful and the topic of the API and JSon took me down a whole other path. I tried building a new project that used what I learned in your tut but instead of accessing APPā€™s info I am using customized queries to retrieve music info. I rebuilt the interface from scratch and converted Results to handle the new JSon elements. All went well, but somehow in the interface I must have tripped something because the NSNotification func tableViewSelectionDidChange(_ notification: NSNotification) never fires when using the up-arrow or down-arrow. I added a button to load the image so I know the code is all good, I also set up an IBAction so the picture changes when selected by mouse-click. I just donā€™t know where to look to get the func to fire. Any suggestions would be appreciated.

fyi, I am using XCode Version 9.0 beta (9M136h) and Swift 4.

Again, thanks for the lesson and introduction into the Appleā€™s API,

Bob

I decided to go another way and not rely on tableViewSelectionDidChange. I set-up a sub-class of NSView, with a protocol that monitors the up/down arrows. These methods call the IBAction I set up for when the user clicks in with the mouse. Works well, not sure if it goes against best practices or not.

I would still like to understand why tableViewSelectionDidChange does not fire, but for now Iā€™ll go with this,

Bob

hey, had the same problem as you and figured it out by myself.

Just add NSTableViewDelegate and NSCollectionViewDelegate to your ViewController class.
What i did was doing an extention and extracted the function to there. Like this:

extension ViewController: NSTableViewDelegate, NSCollectionViewDelegate {
  func tableViewSelectionDidChange(_ notification: Notification) {...}
}

Now the function gets fired just as it should. Hope this helps :slight_smile:
Also, thank you for this great tutorial. Learned a lot of helpful stuff in this one.

One question i have, is there a possibility to improve the scrolling performance on the NSTableView?
When i compare it to, for example finder, itā€™s very laggy and cpu load shoots up to almost 100% when scrolling. Do the bindings get updated all the time while doing so, or why is that?

@dun198 Tobias,

Thanks so much; I tried your code and it worked. I had coded a work around using NSTextFieldDelegate with protocol:

// allows enter key to kick off the search
extension ViewController: NSTextFieldDelegate {
    func control(_ control: NSControl, textView: NSTextView,
                 doCommandBy commandSelector: Selector) -> Bool {
        if commandSelector == #selector(insertNewline(_:)) {
            searchHit(tbxSearch)
        }
        return false
    }
}

//MARK: KeyViewDelegate
extension ViewController: KeyViewDelegate {
    func pressedLeftArrowKey() {
        return
    }
    
    func pressedRightArrowKey() {
        return
    }
    func pressedUpArrowKey() {
        self.tableChanged(self)
    }
    
    func pressedDownArrowKey() {
        self.tableChanged(self)
    }
}

To use this you have to setup a KeyView Class object of NSView.

import Cocoa
let leftArrowKey = 123
let rightArrowKey = 124
let downArrowKey = 125
let upArrowKey = 126
protocol KeyViewDelegate {
    func pressedLeftArrowKey()
    func pressedRightArrowKey()
    func pressedUpArrowKey()
    func pressedDownArrowKey()
}   
class KeyView: NSView {
    var delegate : KeyViewDelegate?
    override func keyDown(with event: NSEvent) {
        let character = Int(event.keyCode)
        switch character {
        case leftArrowKey, rightArrowKey, upArrowKey, downArrowKey:
            break
        default:
            super.keyDown(with: event)
        }
    }    
    override func keyUp(with event: NSEvent) {
        let character = Int(event.keyCode)
        switch character {
        case leftArrowKey:
            delegate?.pressedLeftArrowKey()
        case rightArrowKey:
            delegate?.pressedRightArrowKey()
        case upArrowKey:
            delegate?.pressedUpArrowKey()
        case downArrowKey:
            delegate?.pressedDownArrowKey()
        default:
            super.keyUp(with: event)
        }
    }       
    override var acceptsFirstResponder : Bool {
        return true
    }
}

I got most of this off various stackOverflow postings.

On your scrolling question, Iā€™m not experiencing any slow response on my app. Iā€™m wondering if the scrolling is not causing your issue and that the culprit might be the retrieval of the pictures on from the Internet. You could test this by scrolling through all of your the table entries, which loads each image into the iTunesResults array. Once loaded they will not require reloading. Iif there is vast improvement on your scroll response then it may be the picture loading.

Bob

This tutorial is more than six months old so questions are no longer supported at the moment for it. We will update it as soon as possible. Thank you! :]