Contextual menu with search result for a NSSearchF

2019-08-09 17:14发布

问题:

I would like to have a contextual menu that shows search results as text is entered in a search field. This is an image of the default mail app in OS X that does this. I know how to filter an array of strings according to the search request of the user, but I do not know how to display it this way. I am using Swift and for a Cocoa application. Any help is appreciated.

回答1:

Building from the previous answer, here is a simple Swift 3 class which you can use to automatically handle recent searches. You can add it as a custom class in your storyboard, or directly. It will look like this:

import Cocoa

class RecentMenuSearchField: NSSearchField {

    lazy var searchesMenu: NSMenu = {

        let menu = NSMenu(title: "Recents")

        let recentTitleItem = menu.addItem(withTitle: "Recent Searches", action: nil, keyEquivalent: "")
        recentTitleItem.tag = Int(NSSearchFieldRecentsTitleMenuItemTag)

        let placeholder = menu.addItem(withTitle: "Item", action: nil, keyEquivalent: "")
        placeholder.tag = Int(NSSearchFieldRecentsMenuItemTag)

        menu.addItem( NSMenuItem.separator() )

        let clearItem = menu.addItem(withTitle: "Clear Menu", action: nil, keyEquivalent: "")
        clearItem.tag = Int(NSSearchFieldClearRecentsMenuItemTag)

        let emptyItem = menu.addItem(withTitle: "No Recent Searches", action: nil, keyEquivalent: "")
        emptyItem.tag = Int(NSSearchFieldNoRecentsMenuItemTag)

        return menu
    }()

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        initialize()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        initialize()
    }

    //create menu
    private func initialize() {
        self.searchMenuTemplate = searchesMenu
    }
}


回答2:

NSSearchField searchMenuTemplate (NSMenu) contains Menu Items (NSMenuItem) with specific tags used by the Search Field to populate the Menu.

The recentSearches Array here is just to pass additional String used in complement of Recents Search strings and is not required (I thought that it was to store recent search but no). NSSearchField also clear this Array when the user clears Recents Search.

You can also configure a Menu with category, more info here: Configuring a Search Menu — Apple Developer

Example:

@IBOutlet weak var search: NSSearchField!

/// Array of string containing additional recents search (custom search)
var recentSearches = [String]()

/// Search Field Recents Menu
lazy var searchesMenu: NSMenu = {

    let menu = NSMenu(title: "Recents")

    let i1 = menu.addItem(withTitle: "Recents Search", action: nil, keyEquivalent: "")
    i1.tag = Int(NSSearchFieldRecentsTitleMenuItemTag)

    let i2 = menu.addItem(withTitle: "Item", action: nil, keyEquivalent: "")
    i2.tag = Int(NSSearchFieldRecentsMenuItemTag)

    let i3 = menu.addItem(withTitle: "Clear", action: nil, keyEquivalent: "")
    i3.tag = Int(NSSearchFieldClearRecentsMenuItemTag)

    let i4 = menu.addItem(withTitle: "No Recent Search", action: nil, keyEquivalent: "")
    i4.tag = Int(NSSearchFieldNoRecentsMenuItemTag)

    return menu
}()

override func viewDidLoad() {
    super.viewDidLoad()
    recentSearches = ["Toto","Titi","Tata"]
    search.delegate = self
    search.recentSearches = recentSearches
    search.searchMenuTemplate = searchesMenu
}


回答3:

You need to make an NSMenu Item with special tags that are placeholders so the search field knows where to put recent items, the clear action, etc.

Look at documentation for searchMenuTemplate, and the Tags : NSSearchFieldRecentsMenuItemTag etc.

Basically, make a contextual menu in IB. Drag your search field to use that menu as the searchMenuTemplate, and then populate menu items with the tags you want for clear, recent items, etc.