NSOutlineView, как показать синий контур при щелчке правой кнопкой мыши

Все обсуждали, как избавиться от синего контура при щелчке правой кнопкой мыши... кроме меня.

Вместо этого я пытаюсь отобразить синий контур.

Я не получил никакого контура, когда щелкнул правой кнопкой мыши строку представления контура. Меню появилось, а контура нет.

Вы можете видеть, что синий контур не виден на этом рисунке ниже:

введите здесь описание изображения



Ниже приведено то, чего я пытаюсь достичь.

Синий контур


Обновить

Вот как я реализовал NSMenu. Я создал подкласс NSOutlineView и создал новый протокол для переопределения NSOutlineViewDelegate.

Идея заключалась в том, чтобы упростить задачу, позволив NSOutlineView запрашивать у NSMenu каждый элемент, чтобы мы могли реализовать разные меню для каждого элемента. Это работает, но синий контур не отображается при щелчке правой кнопкой мыши.

KRMenuOutlineView.swift

import Cocoa

@objc protocol KRMenuOutlineViewDelegate: NSOutlineViewDelegate {
    // This method will ask NSMenu for each item in outline view
    func outlineView(_ outlineView: KRMenuOutlineView, menuFor item: Any, event: NSEvent) -> NSMenu?
}

class KRMenuOutlineView: NSOutlineView {

    override var delegate: NSOutlineViewDelegate? {
        didSet {
            if let newValue = delegate {
                /*
                 * Swift doesn't support overriding inherited properties with different type
                 * like Objective C Does, therefore we need internal delegate.
                 */
                internalDelegate = unsafeBitCast(newValue, to: KRMenuOutlineViewDelegate.self)
            } else {
                internalDelegate = nil
            }
        }
    }

    private var internalDelegate: KRMenuOutlineViewDelegate?

    override func menu(for event: NSEvent) -> NSMenu? {
        let point = self.convert(event.locationInWindow, from: nil)
        if let item = self.item(atRow: self.row(at: point)) {
            return self.internalDelegate?.outlineView(self, menuFor: item, event: event)
        }
        return super.menu(for: event)
    }
}

Затем я использую его в своем контроллере представления следующим образом:

KRTreeViewController.swift

extension KRTreeViewController: KRMenuOutlineViewDelegate {
    func outlineView(_ outlineView: KRMenuOutlineView, menuFor item: Any, event: NSEvent) -> NSMenu? {
            let menu = NSMenu(title: "Contextual Menu")
            menu.delegate = self

            let key = String(utf16CodeUnits: [unichar(NSBackspaceCharacter)], count: 1) as String
            let deleteMenuItem = menu.addItem(withTitle: "Delete",
                                              action: #selector(didClickMenuItem(_:)),
                                              keyEquivalent: key)
            deleteMenuItem.representedObject = myItem
            deleteMenuItem.target = self

            return menu
    }

    @objc fileprivate func didClickMenuItem(_ menuItem: NSMenuItem) {
        // ...
    }
}

person Edward Anthony    schedule 29.07.2017    source источник
comment
Мы не знаем, что вы сделали, чтобы синий контур исчез. Как вы реализовали контекстное меню? Вы сделали что-нибудь, чтобы изменить поведение NSOutlineView по умолчанию?   -  person Willeke    schedule 29.07.2017
comment
@Willeke Я обновил свой вопрос.   -  person Edward Anthony    schedule 29.07.2017
comment
Попробуйте позвонить super.menu(for: event) перед возвратом собственного меню.   -  person Willeke    schedule 30.07.2017


Ответы (1)


Как правильно показать контекстное меню:

Если вы создали свое меню с помощью раскадровки: сначала перейдите к раскадровке и добавьте меню в viewController, который содержит layoutView. Затем сделайте его @IBOutlet, чтобы вы могли сослаться на него позже.

В таком методе, как viewDidLoad(), добавьте меню в layoutView, вызвав

outlineView.menu = myMenu

где myMenu может быть тем, что вы создали в Интерфейсном Разработчике или в коде.

Теперь вы можете запустить приложение и должны увидеть синий контур вокруг ячейки.

Теперь проблема в том, что вы не знаете, какую ячейку щелкнул пользователь. Чтобы это исправить, назначьте себя делегатом myMenu и примите протокол NSMenuDelegate.

func menuNeedsUpdate(_ menu: NSMenu) {
    let row = self.outlineView.clickedRow

    guard row != -1 else { return }
    for item in menu.items {
        item.representedObject = row
    }
}

Здесь вы можете делать все, что вам нужно. Эта реализация устанавливает rowIndex как представленный объект каждого пункта меню. Имейте в виду, что это работает только со статическими схемами просмотра (теми, которые не меняются в фоновом режиме) и меню, которые имеют только один уровень глубины.

Вы также можете сохранить индекс или объект, представленный ячейкой (если layoutView не является статическим) в локальной переменной.

person mangerlahn    schedule 29.07.2017
comment
Спасибо за ваш ответ, используя это решение, как мы можем ограничить отображение меню только для определенного элемента в NSOutlineView? Я также обновил свой вопрос, показав реализацию кода. - person Edward Anthony; 29.07.2017
comment
Что делать, если я не создал меню с помощью раскадровки? Я прокомментировал все изменения в своем подклассе представления структуры, и синий контур все еще не отображается... - person Nicolas Miari; 28.08.2017
comment
@Nicolas Miari: Вы можете просто создать меню в коде. Например, в viewDidLoad() создайте свое меню и вызовите outlineView.menu = myMenu. Остальное должно быть как описано выше. - person mangerlahn; 30.08.2017
comment
@mangerlahn Да, спасибо; Я понял это в конце концов. Я добавил NSMenu с одним фиктивным элементом, а затем переопределил menu(for:), чтобы иметь меню для конкретных ячеек. Само по себе переопределение не сработает; свойство меню должно быть установлено в непустое меню. - person Nicolas Miari; 30.08.2017