(针对OS X 10.10,最新版本的Xcode)
我有一个NSButton子类。
该按钮需要响应鼠标悬停/悬停,就像一个 Web 按钮一样。
该按钮需要以圆形出现。 因此,-setImage: 和 -setAlternateImage: 是用圆形图形来调用的。
问题 1:当在 NSButton 矩形内的任意位置单击鼠标时,NSButton 的默认行为会导致正命中测试。这包括按钮圆形区域之外的鼠标单击。
建议的解决方案:覆盖-hitTest:计算点击与按钮中心的距离。如果距离>按钮的半径,则表示我们没有有效的点击。
好的,好的。这为我们提供了对鼠标单击圆形按钮的准确碰撞检测。
问题2:如何处理鼠标悬停/翻转,并相应地更改按钮的外观。
建议的解决方案:向按钮添加一个 NSTrackingArea,然后覆盖 -mouseEntered: 和 -mouseExited:这样我们就可以在鼠标移动到按钮上时更改按钮图像和交替图像。
好的,好的。这适用于矩形。
但...... NSTrackingArea适用于矩形,而不是任意区域,如圆形。更糟糕的是,它不尊重 -hitTest:
建议的解决方案:将循环碰撞检测代码添加到 -mouseEntered: 和 -mouseExited:
但。。。
-mouseEntered: 和 -mouseExited: 每个按钮进入/退出仅调用一次,而不是连续调用。这意味着,如果鼠标刚好移动到按钮的矩形区域,但距离不足以进入按钮的圆形区域,则将调用 -mouseEntered: 。但它不会再次被调用,直到 -mouseExited: 被调用。鼠标进一步移动到圆形区域将不起作用。(不会发生展期。
即,使用这两种方法不可能通过准确的鼠标位置信息连续更新按钮的状态。
问题:有谁知道如何实现悬停和按下非矩形按钮?
[编辑 - 多亏了可可,解决了。
解决方案步骤:
在按钮中添加一个 NSTrackingArea,(至少)包含 NSTrackingMouseEnteredAndExited 和 NSTrackingMouseMoveed(至少)选项。
实现 -hitTest:以便对按钮按下进行准确(例如循环)命中测试。我们不会在此处更改按钮的外观。
实现 -mouseEntered: 和 -mouseExited:
在 -mouseEntered 中执行(圆形)碰撞检测,通过 -setImage: 相应地更改按钮的外观(或以其他方式标记对绘图策略的更改。
在 -mouseExited 上:将按钮的外观设置回其默认值。
但是,-mouseEntered: 和 -mouseExited: 不会连续调用。因此,我们还需要实现 -mouseMoved:
但是,我们必须在窗口上调用 -setAcceptsMouseMovedEvents:YES,以便在按钮上调用 -mouseMoved: 方法。
将(圆形)碰撞检测和外观更改为 -mouseMoving 也添加。
使用NSTrackingArea
是可行的方法,对于非矩形形状,您必须添加自定义代码来对自定义几何图形执行命中测试。
不清楚为什么当鼠标悬停在您的形状上方时要连续设置相同的图像?
无论如何,有mouseMoved
:当鼠标在您的跟踪区域内移动时收到通知。不过,请检查文档 - 您可能需要在视图或窗口中设置一些额外的标志,因为默认行为可能不会发送移动的消息,以免在没有人监听的情况下用大量消息阻塞框架。
因此,您确实希望使用跟踪区域和鼠标事件。这些可以帮助您知道是否要测试某个点。
在大多数情况下,您可能实际上想要比图像稍大的区域。它看起来更自然。
如果按钮是基于图像的,请使用此 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:context:hints:flipped:
如果您的按钮是基于路径的,NSBezierPath 和 CGPath 有自己的包含点类型的解决方案。
在这里度过你的时间。这是人们在UI中拥有最多的体验之一,所以如果它不是正确的,它会感觉很笨拙。