Mac Cocoa:如何区分NSCrollWheel事件是来自鼠标还是触控板



在我的应用程序中,我希望只通过鼠标的滚轮动作进行滚动,而不是通过触控板上的两个手指手势进行滚动。基本上,我试图确定scrollWheelEvent是从鼠标还是触控板生成的,在-(void)scrollWheel:(NSEvent*)theEvent方法内部。据我目前所知,似乎没有直接的方法来实现这一点。

我尝试了在(void)beginGestureWithEvent:(NSEvent*)event内将布尔变量设置为true和false的方法;和-(void)endGestureWithEvent:(NSEvent*)事件;但这不是一个解决方案,因为在endGestureWithEvent:方法被调用之后,scrollWheel:方法被多次调用。

这是我的代码:

$BOOL fromTrackPad = NO;
-(void)beginGestureWithEvent:(NSEvent *)event;
{
fromTrackPad = YES;    
}
-(void) endGestureWithEvent:(NSEvent *)event;
{
fromTrackPad = NO;    
}
- (void)scrollWheel:(NSEvent *)theEvent
{
if(!fromTrackPad)
{
//then do scrolling
}
else 
{
//then don't scroll
}
}

我知道这不是标准,但这是我的要求。有人知道怎么做吗??谢谢

-[NSEvent momentumPhase]就是解决方案。因此,在beginGesture和endGesture事件之间从触控板生成的事件为-[NSEvent phase]返回除NSEventPhaseNone之外的值,并且在endGestule事件之后生成的触控板事件为-[NSEvent momentumPhase]返回除NSEventPhaseNone之外的值。代码如下,

- (void)scrollWheel:(NSEvent *)theEvent
{
if(([theEvent momentumPhase] != NSEventPhaseNone) || [theEvent phase] != NSEventPhaseNone))
{
//theEvent is from trackpad           
}
else 
{
//theEvent is from mouse
}
}

您可以使用[event hasPreciseScrollingDeltas]进行区分。它是在OS X Lion中添加的。它区分了线滚动(鼠标滚轮)和像素滚动(轨迹板、魔术鼠标)事件。

我在Swift中的答案,但对于Objective C逻辑是相同的:

override func scrollWheel(with event: NSEvent) {
if event.subtype == .mouseEvent {
// mouse
} else {
// trackpad
}
}

@AProgrammer给出的答案可能不可用。因为魔术鼠标生成的滚动轮事件的相位值为:开始、更改和结束。强大的鼠标生成的滚动轮事件对于相位和动量都是none值。因此,该方法只能区分强力鼠标、魔术鼠标和触控板。

我刚刚实现了触摸事件处理,并跟踪触摸次数。当触发scrollWheel事件时,我可以检测到触控板,因为会有非零触摸计数(我现在测试两个手指)。魔术鼠标触摸不会触发触摸事件,因此触摸次数始终为零。

当然,这是一种测试触控板与鼠标滚轮的方法,它们在未来可能会失败。对于我没有测试过的某些配置,它也可能失败——我使用的是MacBook Pro和Magic Mouse——不要把它视为认可或苹果可以接受的答案。但就目前而言,它似乎确实奏效了。当我在触控板上拖动手指时,我确实看到一个事件的触摸次数为零,所以这也可能是一个问题。

[编辑]我刚刚遇到动量阶段会导致问题,因为触摸计数为零,但scrollWheel事件仍然不断出现。问题是触摸计数为1,scrollWheels事件出现,但动量阶段原始值为零。这一事件使得很难检测平移是否结束以及鼠标滚轮是否正在使用。计时器是唯一的解决方案,因为似乎没有数学或事件数据来知道动量事件与前两个手指"相关联";pan";事件

这是我今天在NSView课上写的代码:

var touchCount = 0
override func touchesBegan( with event: NSEvent ) {
getTouchCount( with: event )
}
override func touchesEnded( with event: NSEvent ) {
getTouchCount( with: event )
}
override func touchesCancelled(with event: NSEvent) {
getTouchCount( with: event )
}
private func getTouchCount( with event: NSEvent ) {
touchCount = 0
for touch in event.allTouches() {
if !touch.phase.oneOf( .cancelled, .ended ) {
touchCount += 1
}
}
}
override func scrollWheel( with event: NSEvent ) {
if touchCount == 2 {
onPan( using: Point( event.scrollingDeltaX, -event.scrollingDeltaY ) ) // My pan function
} else {
zoom( delta: event.scrollingDeltaY ) // My zoom function
}
}

好的,这里有一些新的代码似乎可以工作。它考虑到了这样一个事实,即在手指松开和动量阶段开始之间可能会发生一两个事件。我暂时猜到了延迟。我将纳秒值调整为秒,因为我正在打印它们,也因为它在检查事件之间经过的时间时使代码更容易理解。

var lastScrollWheelTick = Double( DispatchTime.now().uptimeNanoseconds ) / 1000000000.0
override func scrollWheel( with event: NSEvent ) {
let tick = Double( DispatchTime.now().uptimeNanoseconds ) / 1000000000.0
let timeSinceLastEvent = tick - lastScrollWheelTick
lastScrollWheelTick = tick
if wasTwoFingers {
lastPanTick = tick
// Test for touch count of 2, some momentum, or if thre has not been much time sinc ethe last event, which suggests
// that this event is a zero-or-one-finger event or thw weird no-fingers-no-momentum event that shows up before the
// momentum actually starts. An event count might also be useful but might be overkill. The onoy problem with this
// is that using the mouse top (on Magic Mouse) does panning if the momentun hasn't stopped for long enough, which
// it won't have.
if touchCount == 2 || event.momentumPhase.rawValue != 0 || timeSinceLastEvent < 0.1 {
let adjustment = 1.0
dragPreviousPoint = Point()
onPan( using: Point( event.scrollingDeltaX * adjustment, -event.scrollingDeltaY * adjustment ) )
return
} else {
wasTwoFingers = false
}
}

let point = convert( event.locationInWindow, from: nil )
mousePoint = Point( cgPoint: point )

zoom( delta: event.scrollingDeltaY )
}

相关内容

  • 没有找到相关文章

最新更新