NSButton - Проверка нажатия кнопки мыши при наведении курсора на кнопки нестандартной формы

(Ориентация на OS X 10.10, последняя версия Xcode)

У меня есть подкласс NSButton.

Кнопка должна реагировать на наведение / наведение курсора мыши, например веб-кнопку.

Кнопка должна иметь форму круга. Соответственно, -setImage: и -setAlternateImage: вызываются с графикой круглой формы.

Проблема 1: поведение NSButton по умолчанию приводит к положительному результату проверки попадания при щелчке мышью в любом месте в пределах поля NSButton. Сюда входят щелчки мышью за пределами круглой области кнопки.

Предлагаемое решение: переопределить -hitTest: для расчета расстояния клика от центра кнопки. Если расстояние> радиуса кнопки, у нас нет действительного щелчка.

Хорошо. Это обеспечивает точное обнаружение столкновений при щелчках мышью по круглым кнопкам.

Проблема 2: как обрабатывать наведение / наведение курсора мыши и соответствующим образом изменять внешний вид кнопки.

Предлагаемое решение: добавьте NSTrackingArea к кнопке, а затем переопределите -mouseEntered: и -mouseExited:, чтобы мы могли изменить изображение кнопки и alternateImage. когда мышь перемещается по кнопке.

Хорошо. Это работает для прямоугольников.

Но ... NSTrackingArea работает с прямоугольниками, а не с произвольными областями, такими как круг. Что еще хуже, -hitTest:

Предлагаемое решение: добавьте код обнаружения кругового столкновения в -mouseEntered: и -mouseExited:

Но ...

-mouseEntered: и -mouseExited: вызываются только ОДИН РАЗ для каждой кнопки входа / выхода, а не постоянно. Это означает, что если мышь перемещается только в прямоугольную область кнопки, но недостаточно далеко, чтобы она вошла в круговую область кнопки, будет вызван -mouseEntered:. Но он не будет вызываться снова, пока не будет вызван -mouseExited:. Дальнейшее перемещение мыши в круговую область не повлияет. (Ролловера не произойдет.)

то есть невозможно постоянно обновлять состояние кнопки с помощью точной информации о положении мыши с помощью этих двух методов.

ВОПРОС: кто-нибудь знает, как реализовать наведение и нажатие на непрямоугольную кнопку?

[изменить - РЕШЕНО благодаря cacau.]

Шаги решения:

  1. Добавьте к кнопке NSTrackingArea с (как минимум) параметрами NSTrackingMouseEnteredAndExited и NSTrackingMouseMoved.

  2. Реализуйте -hitTest: для точного (например, кругового) тестирования нажатия кнопок. Здесь мы не меняем внешний вид кнопки.

  3. Реализуйте -mouseEntered: и -mouseExited:

  4. Выполните (круговое) обнаружение столкновений в -mouseEntered, соответствующим образом изменив внешний вид кнопки с помощью -setImage: (или иным образом отметив изменения в стратегии рисования.)

  5. На -mouseExited: верните внешний вид кнопки по умолчанию.

  6. Но -mouseEntered: и -mouseExited: не вызываются постоянно. Поэтому нам также необходимо реализовать -mouseMoved:

  7. Но мы должны вызвать -setAcceptsMouseMovedEvents: YES в окне, чтобы для кнопки вызывался метод -mouseMoved:.

  8. Также добавьте (круговое) обнаружение столкновений и изменение внешнего вида в -mouseMoved.


person Womble    schedule 13.01.2015    source источник
comment
Вы на самом деле не настроили таргетинг на 10.0, не так ли? :-)   -  person cacau    schedule 13.01.2015


Ответы (2)


Использование NSTrackingArea - это правильный путь, для непрямоугольных форм вам нужно будет добавить собственный код для выполнения проверки попадания для ваших пользовательских геометрий.

Непонятно, почему вы хотите постоянно устанавливать одно и то же изображение, когда курсор мыши находится над вашей фигурой?

В любом случае, есть mouseMoved: чтобы получать уведомления, когда мышь перемещается в пределах вашей области отслеживания. Тем не менее, проверьте документацию - вам может потребоваться установить некоторые дополнительные флаги в представлении или окне, поскольку поведение по умолчанию может не отправлять перемещенные сообщения, чтобы не забивать фреймворк огромным количеством сообщений в случае, если никто не слушает.

person cacau    schedule 13.01.2015
comment
Спасибо за помощь! У меня был успех с mouseMoved: и я скорректирую свой вопрос, чтобы помочь будущим читателям. Re: меняя изображения, я сначала проверяю существующее изображение, чтобы избежать расточительной настройки. - person Womble; 14.01.2015

Итак, вы хотите использовать области отслеживания и события мыши. Это поможет вам узнать, хотите ли вы поразить точку или нет.

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

Если ваша кнопка основана на изображении, используйте этот метод NSImage для проверки попадания

hitTestRect: withImageDestinationRect: context: hints: flipped:

Помните, что прямоугольник может быть размером с точку.

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSImage_Class/index.html#//apple_ref/occ/instm/NSImage/hitTestRect:withImageDestinationRect:withImageDestinationRect::hints:flipped:

Если ваша кнопка основана на пути, NSBezierPath и CGPath имеют свои собственные решения, содержащие точки.

Проведите здесь свое время. Это один из наиболее важных моментов в пользовательском интерфейсе, поэтому, если он не совсем правильный, он будет казаться неуклюжим.

person uchuugaka    schedule 14.01.2015