【问题标题】:Action of NSToolbarItem not triggered未触发 NSToolbarItem 的操作
【发布时间】:2017-08-09 18:00:08
【问题描述】:

我是 macOS 编程的新手,想为 NSToolbarItem 实现一个自定义类。每次按下该项目时,我都想更改该类的属性。这是一个示例代码:

class CustomToolbarButton: NSToolbarItem
{
   override init(itemIdentifier: String)
   {
      super.init(itemIdentifier: itemIdentifier)
      super.target = self
      super.action = #selector(reactToPress)
   }

    func reactToPress(sender: NSToolbarItem)
    {
      toggled_property = !toggled_property
      print("Item pressed")
    }

    private(set) var toggled_property = true;
}

这个类被插入到故事板的工具栏中。我确保将身份检查器中的类说明符更改为 CustomToolbarButton。但是,该操作似乎从未被触发,因为“按下的项目”从未出现在控制台输出中。

我还尝试通过以下方式声明“reactToPress”函数:

func reactToPress()
@objc func reactToPress()
@objc func reactToPress(sender: NSToolbarItem)

但还是没有成功。

【问题讨论】:

    标签: swift cocoa


    【解决方案1】:

    您需要对项目的目标进行非弱引用。 试试这个:

    // Define this in your class.
    static let itemTarget = CustomToolbarButton(itemIdentifier: "myButton")
    
    // and use it when setting the target in the constructor.
    self.target = CustomToolbarButton.itemTarget
    

    来自 docs.swift.org “弱引用是一种不会对其引用的实例保持强控制的引用,因此不会阻止 ARC 处理引用的实例。”

    如果对实例的所有引用都是弱引用,则会自动创建并释放该项目。您需要至少一个非弱引用来保留实例。

    推荐阅读: https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html

    另外,就像你说的,@objc 指令在动作函数中是必需的。 这里有一些有用的链接。

    '#selector' 指的是没有暴露给 Objective-C 的方法 '#selector' refers to a method that is not exposed to Objective-C

    查看选择器表达式 https://docs.swift.org/swift-book/ReferenceManual/Expressions.html

    最后这里是一个关于 Xcode 10.3 和 Swift 5 的工作示例:

    //  Created by Juan Miguel Pallares Numa on 9/12/19.
    //  Copyright © 2019 Juan Miguel Pallares Numa. All rights reserved.
    
    import Cocoa
    
    @NSApplicationMain
    class AppDelegate: NSObject, NSApplicationDelegate {
    
        // Strong reference.
        var myWindowController = WindowController()
    
        func applicationDidFinishLaunching(_ aNotification: Notification) {
            myWindowController.makeWindowKeyAndOrderFront()
        }
    }
    
    import Cocoa
    
    class ToolbarController: NSObject, NSToolbarDelegate {
    
        let identifiers = [NSToolbarItem.Identifier("myIdentifier")]
        let toolbar = NSToolbar()
        let toolbarItem: NSToolbarItem
    
        override init() {
            toolbarItem = NSToolbarItem(itemIdentifier: identifiers[0])
            super.init()
            toolbar.delegate = self
    
            toolbarItem.label = "Print My Console Message"
            toolbarItem.target = self
            toolbarItem.action = #selector(
                ToolbarController.toolbarAction(_:))
            toolbarItem.image = NSImage(named: NSImage.applicationIconName)!
        }
    
        @objc func toolbarAction(_ sender: Any?) {
            print("Hello world")
        }
    
        func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
            return identifiers
        }
    
        func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
            return identifiers
        }
    
        func toolbarSelectableItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
            return identifiers
        }
    
        func toolbar(_ toolbar: NSToolbar,
                     itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
                     willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
    
            return toolbarItem
        }
    }
    
    import Cocoa
    
    class WindowController: NSWindowController {
    
        var toolbarController = ToolbarController()
    
        convenience init() {
            let window = NSWindow(
                contentViewController: ViewController())
    
            self.init(window: window)
    
            window.toolbar = toolbarController.toolbar
        }
    
        func makeWindowKeyAndOrderFront() {
            window?.makeKeyAndOrderFront(nil)
        }
    }
    
    import Cocoa
    
    class ViewController: NSViewController {
    
        override func loadView() {
            view = NSView(frame: NSRect(x: 0, y: 0, width: 400, height: 300))
        }
    }
    

    【讨论】:

      【解决方案2】:

      尝试符合 Objective-C 的语法

      super.action = #selector(reactToPress(_:))
      

      func reactToPress(_ sender: NSToolbarItem)
      

      在 Swift 4 中,您必须明确添加 @objc 属性

      @objc func reactToPress(_ sender: NSToolbarItem)
      

      【讨论】:

      • 感谢您的建议。可惜没有结果。我不知道这是否重要,但我使用 Swift 4
      • 我更新了答案。但是 Swift 4 很可能 init(itemIdentifier 方法不适用于 String 参数。
      • 我在 init 方法中添加了一个控制台输出,它被触发了。但是,即使我添加了@objc,也不会调用操作函数。
      • 其实init方法在Swift 4中已经改为init(itemIdentifier: NSToolbarItem.Identifier)
      • 天哪,对不起...我实际上使用的是swift 3.1
      猜你喜欢
      • 2014-09-18
      • 2014-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-18
      • 1970-01-01
      相关资源
      最近更新 更多