Sender of IBAction is NSButton instead of NSToolbarItem

I have a toolbar in my Mac app that uses storyboards. I added a button to the toolbar. I connected the toolbar item for the button to the window controller's First Responder, with the action being an IBAction I wrote.


In Interface Builder's Connections Inspector, I can see the toolbar item has a sent action to First Responder. The button itself has no sent actions.


Here's the code for my IBAction.


@IBAction func addTagFromToolbar(_ sender: NSToolbarItem) {
        let tagTitle = sender.label
        createTag(name: tagTitle)
       
        NotificationCenter.default.post(name: NSText.didChangeNotification, object: textView)
}


When I click the button, an exception is thrown on Line 2, with the following error message appearing the debug console:


[NSButton label]: unrecognized selector sent to instance 0x600003508580


Even though I set the type of sender to NSToolbarItem and made sure the toolbar item (and not the button) is connected to First Responder, the app is using the button as the sender.


What do I have to do to make the IBAction's sender the toolbar item instead of the button?

What I did do:

Added a button in the list of Toolbar's view

click on the toolbar

A window opens

the added button shows.

Drag it into Default toolbar items just below

The button now shows in position in the toolbar and it is a toolBarItem

Control drag from the button icon in the top list (Allowed Toolbar items) to code to define an action

    @IBAction func test(_ sender: NSToolbarItem) {
        print("Tested toolbar item")
    }


This works.

Are you able to print the value of sender.label without an exception firing?


I have no problem with the IBAction firing. My problem is the sender ends up being an instance of NSButton instead of NSToolbarItem.

You're right.


I tested the following


    @IBAction func test(_ sender: NSToolbarItem) {
        print("Tested toolbar item")
        print(sender)
        print(sender.label)
    }



I get

Tested toolbar item

<NSButton: 0x604000140e70>

So, sender is an NSButton, despite the IBAction declaration

And of course, crashes when trying to access the label

-[NSButton label]: unrecognized selector sent to instance 0x604000140e70


But trying to access sender.title causes a compiler error

        print(sender.title)

Value of type 'NSToolbarItem' has no member 'title'

Which means sender is seen as an NSToolbarItem !!!!


Pretty strange, compiler sees something different from the execution engine.


Just by curiosity, I tried this:

    @IBAction func test(_ sender: Any) {    // NSToolbarItem
        print("Tested toolbar item")
        print(sender)
        print((sender as! NSToolbarItem).label) // print(sender.label)
        print((sender as! NSButton).title) // print(sender.title)
    }

And "logically" got a crash at line 4

Could not cast value of type 'NSButton' (0x7fffa0df88b0) to 'NSToolbarItem'


That's looks like a bug you should file for.


Maybe interested in reading this, which tends to show Toolbars and storyboard do not work perfectly together.

w ww.extelligentcocoa.org/toolbar-tutorial/

Thanks for confirming the issue. I filed a bug report.


Bug #: 47292220

Apple closed my bug report, saying this is intended behavior.


If I have a button as a toolbar item, how do I access the toolbar item's label when clicking the button?

Could you post what you wrote exactly in the bug report ?

Here's the bug report text.


Area:

AppKit


Summary:


I add a button to a Mac toolbar. I write an IBAction with a sender type of NSToolbarItem. I connect the toolbar item to the IBAction. When the IBAction fires, the sender is treated as NSButton instead of NSToolbarItem so exceptions are thrown when I try to access properties of the NSToolbarItem class.


Steps to Reproduce:


1. Create Cocoa application project with a storyboard.

2. Add a button to the toolbar.

3. Add an IBAction to the code, with the sender type being NSToolbarItem.

4. Write code that accesses the sender's label.


print(sender.label)


5. Connect the toolbar item to the IBAction.

6. Run the app.

7. Click the toolbar button.


Expected Results:


The code runs without issues. If you print sender.label, the value of the label is printed to the console.


Actual Results:


An exception is thrown, with the following message appearing in Xcode's debug console:


[NSButton label]: unrecognized selector sent to instance 0x600003508580


The sender is NSButton instead of NSToolbarItem.


Version/Build:


Xcode 10.1 (10B61)


Configuration:


A 27-inch Late 2015 iMac running macOS 10.14.2.


Additional Notes:


Another person was able to duplicate the issue in the following thread from Apple's developer forums:


https://forums.developer.apple.com/thread/112749

I think you should add the following test, which shows sender is either seen as NSButton or NSToolBarItem


Test the following


@IBAction func test(_ sender: NSToolbarItem) {

print("Tested toolbar item")

print(sender)

print(sender.label)

}


Which yields:

Tested toolbar item

<NSButton: 0x604000140e70> // So, sender is an NSButton, despite the IBAction declaration

And of course, crashes when trying to access the label

-[NSButton label]: unrecognized selector sent to instance 0x604000140e70


But trying to access sender.title causes a compiler error : Value of type 'NSToolbarItem' has no member 'title'

print(sender.title) // So, sender is seen as an NSToolbarItem !!!!

Sender of IBAction is NSButton instead of NSToolbarItem
 
 
Q