I just implemented touch event handling and I keep track of the touch count. When a scrollWheel event is triggered, I can detect the trackpad because there will be a non-zero touch count (I test for two fingers at the moment). The Magic Mouse touches do not trigger touch events so the touch count is always zero.
This is, of course, a way to test for trackpad vs. mouse scroll wheel that might fail in the future. It might also fail for certain configurations that I have not tested - I'm using a MacBook Pro and Magic Mouse - do don't take this as a sanctioned or Apple-acceptable answer. But for now, it does seem to work. I did see the touch count go to zero for a single event while I was dragging my fingers across the trackpad so that could also be a problem.
[Edit] I just encountered momentum phases that cause problems since the touch count goes to zero but scrollWheel events still keep coming in. The problem is that the touch count goes to one and a scrollWheel event shows up but with a momentum phase raw value of zero. This one event makes it difficult to detect if the pan is ending and a mouse wheel is being used. A timer is the only solution since there seems to be no math or event data to know that momentum events are tied to the previous two-finger "pan" event.
Here's the code I wrote today in my NSView class:
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
}
}
Ok, so here is some new code that seem to work. It takes into account the fact that there might be an event or two that happen between when the fingers are released and the momentum phase starts. I guessed at the delay for now. I adjust the nanosecond values to seconds because I was printing them out and also because it makes the code a little more understandable when checking how long has passed between events.
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 )
}