Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/macos/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 未为NSMenu调用ValidateNuItem或menuWillOpen_Swift_Macos_Cocoa_Nsmenu_Nsmenuitem - Fatal编程技术网

Swift 未为NSMenu调用ValidateNuItem或menuWillOpen

Swift 未为NSMenu调用ValidateNuItem或menuWillOpen,swift,macos,cocoa,nsmenu,nsmenuitem,Swift,Macos,Cocoa,Nsmenu,Nsmenuitem,我的Mac应用程序有一个NSMenu,它的委托函数validateNuitem和menuWillOpen从不被调用。到目前为止,在线解决方案都没有起到作用 看来我做的每件事都是对的: 菜单项的选择器在同一类中 管理它的类从NSMenuDelegate继承 我想描述我的问题的最好方法是发布相关代码。任何帮助都将不胜感激 import Cocoa class UIManager: NSObject, NSMenuDelegate { var statusBarItem = NS

我的Mac应用程序有一个NSMenu,它的委托函数
validateNuitem
menuWillOpen
从不被调用。到目前为止,在线解决方案都没有起到作用

看来我做的每件事都是对的:

  • 菜单项的选择器在同一类中
  • 管理它的类从NSMenuDelegate继承
我想描述我的问题的最好方法是发布相关代码。任何帮助都将不胜感激

import Cocoa

class UIManager: NSObject, NSMenuDelegate {    
    var statusBarItem = NSStatusBar.system().statusItem(withLength: -2)
    var statusBarMenu = NSMenu()
    var titleMenuItem = NSMenuItem()
    var descriptionMenuItem = NSMenuItem()

    // ...

    override init() {            
        super.init()

        createStatusBarMenu()
    }

    // ...

    func createStatusBarMenu() {
        // Status bar icon
        guard let icon = NSImage(named: "iconFrame44")
            else { NSLog("Error setting status bar icon image."); return }
        icon.isTemplate = true
        statusBarItem.image = icon

        // Create Submenu items
        let viewOnRedditMenuItem = NSMenuItem(title: "View on Reddit...", action: #selector(viewOnRedditAction), keyEquivalent: "")
        let saveThisImageMenuItem = NSMenuItem(title: "Save This Image...", action: #selector(saveThisImageAction), keyEquivalent: "")

        // Add to title submenu
        let titleSubmenu = NSMenu(title: "")
        titleSubmenu.addItem(descriptionMenuItem)
        titleSubmenu.addItem(NSMenuItem.separator())
        titleSubmenu.addItem(viewOnRedditMenuItem)
        titleSubmenu.addItem(saveThisImageMenuItem)

        // Create main menu items
        titleMenuItem = NSMenuItem(title: "No Wallpaperer Image", action: nil, keyEquivalent: "")
        titleMenuItem.submenu = titleSubmenu
        getNewWallpaperMenuItem = NSMenuItem(title: "Update Now", action: #selector(getNewWallpaperAction), keyEquivalent: "")
        let preferencesMenuItem = NSMenuItem(title: "Preferences...", action: #selector(preferencesAction), keyEquivalent: "")
        let quitMenuItem = NSMenuItem(title: "Quit Wallpaperer", action: #selector(quitAction), keyEquivalent: "")

        // Add to main menu
        let statusBarMenu = NSMenu(title: "")
        statusBarMenu.addItem(titleMenuItem)
        statusBarMenu.addItem(NSMenuItem.separator())
        statusBarMenu.addItem(getNewWallpaperMenuItem)
        statusBarMenu.addItem(NSMenuItem.separator())
        statusBarMenu.addItem(preferencesMenuItem)
        statusBarMenu.addItem(quitMenuItem)

        statusBarItem.menu = statusBarMenu
    }

    // ...

    // Called whenever the menu is about to show. we use it to change the menu based on the current UI mode (offline/updating/etc)
    override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
        NSLog("Validating menu item")
        if (menuItem == getNewWallpaperMenuItem) {
            if wallpaperUpdater!.state == .Busy {
                DispatchQueue.main.async {
                    self.getNewWallpaperMenuItem.title = "Updating Wallpaper..."
                }
                return false
            } else if wallpaperUpdater!.state == .Offline {
                DispatchQueue.main.async {
                    self.getNewWallpaperMenuItem.title = "No Internet Connection"
                }
                return false
            } else {
                DispatchQueue.main.async {
                    self.preferencesViewController.updateNowButton.title = "Update Now"
                }
                return true
            }
        }

        return true
    }

    // Whenever the menu is opened, we update the submitted time
    func menuWillOpen(_ menu: NSMenu) {
        NSLog("Menu will open")
        if !noWallpapererImageMode {
            DispatchQueue.main.async {
                self.descriptionMenuItem.title = "Submitted \(self.dateSimplifier(self.updateManager!.thisPost.attributes.created_utc as Date)) by \(self.updateManager!.thisPost.attributes.author) to /r/\(self.updateManager!.thisPost.attributes.subreddit)"
            }
        }
    }

    // ...

    // MARK: User-initiated actions

    func viewOnRedditAction() {
        guard let url = URL(string: "http://www.reddit.com\(updateManager!.thisPost.permalink)")
            else { NSLog("Could not convert post permalink to URL."); return }
        NSWorkspace.shared().open(url)
    }

    // Present a save panel to let the user save the current wallpaper
    func saveThisImageAction() {
        DispatchQueue.main.async {
            let savePanel = NSSavePanel()
            savePanel.makeKeyAndOrderFront(self)

            savePanel.nameFieldStringValue = self.updateManager!.thisPost.id + ".png"
            let result = savePanel.runModal()

            if result == NSFileHandlingPanelOKButton {
                let exportedFileURL = savePanel.url!
                guard let lastImagePath = UserDefaults.standard.string(forKey: "lastImagePath")
                    else { NSLog("Error getting last post ID from persistent storage."); return }
                let imageData = try! Data(contentsOf: URL(fileURLWithPath: lastImagePath))
                if (try? imageData.write(to: exportedFileURL, options: [.atomic])) == nil {
                    NSLog("Error saving image to user-specified folder.")
                }
            }
        }
    }

    func getNewWallpaperAction() {
        updateManager!.refreshAndReschedule(userInitiated: true)
    }

    func preferencesAction() {
        preferencesWindow.makeKeyAndOrderFront(nil)
        NSApp.activateIgnoringOtherApps(true)
    }

    func quitAction() {
        NSApplication.shared().terminate(self)
    }
}

menuWillOpen:
属于
NSMenuDelegate
协议;要调用该菜单,需要一名代表:

let statusBarMenu = NSMenu(title: "")
statusBarMenu.delegate = self

validateNuitem:
属于
NSMenuValidation
非正式协议;要调用它,相关菜单项必须具有
目标
。以下段落摘自苹果的文档:

使用自动菜单启用时,每当发生用户事件时,NSMenu都会更新每个菜单项的状态。要更新菜单项的状态,NSMenu首先确定该项的目标,然后确定目标是实现ValidateNuItem:还是validateUserInterfaceItem:(按该顺序)

上述(已接受的)答案说明必须设定目标,这有点误导。不需要设定目标。例如,您还可以创建第一响应者,而无需显式设置目标

详细信息可在appel文档中找到,可在以下位置找到:

但在使用swift时,有一个棘手的部分:

如果未调用ValidateNuItem,则确保您的类不仅声明了与NSMenuDelegate的一致性,还声明了与NSMenuItemValidation的一致性

class SomeClass: NSMenuDelegate, NSMenuItemValidation {
...
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
   return true // or whatever, on whichever condition
}
}

您似乎没有在代码中的任何位置设置菜单的委托。谢谢,这解决了menuWillOpen问题。不过,这并不能解决ValidateNuitem的问题。谢谢。在我的例子中,我添加了行
statusBarMenu.delegate=self
,并在初始化每个具有操作的菜单项后添加了
myMenuItem.target=self
。如果菜单项没有操作,是否不会调用ValidateNuItem?在目标中声明对NSMenuItemValidation的支持(即:类MyTargetClass:NSObject、NSMenuItemValidation…)。很多问题都源于swift与obj-c完全不同的事实……它一直在折磨我。最可悲的是,为什么我必须验证一个已经在故事板上启用的菜单项,而我一直想启用它?只有当我想禁用某些菜单项时才需要它。苹果和他们的精神病患者。嗯,validateNuItem()在您希望根据第一响应者的上下文重命名菜单项时非常方便。
class SomeClass: NSMenuDelegate, NSMenuItemValidation {
...
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
   return true // or whatever, on whichever condition
}
}