Actually I fixed it better here:
import AppKit
import SwiftUI
struct CaptureVerticalScrollWheelModifier: ViewModifier {
func body(content: Content) -> some View {
content
.background(ScrollWheelHandlerView())
}
struct ScrollWheelHandlerView: NSViewRepresentable {
func makeNSView(context _: Context) -> NSView {
let view = ScrollWheelReceivingView()
return view
}
func updateNSView(_: NSView, context _: Context) {}
}
class ScrollWheelReceivingView: NSView {
private var scrollVelocity: CGFloat = 0
private var decelerationTimer: Timer?
override var acceptsFirstResponder: Bool { true }
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
window?.makeFirstResponder(self)
}
// Don't capture vertical scroll for precise scrolling (e.g. magic mouse/trackpad), and don't capture if we are already scrolling horizontally (e.g. shift + scroll). If we don't do this, we get very unpredictable behavior
override func scrollWheel(with event: NSEvent) {
if event.hasPreciseScrollingDeltas || abs(event.scrollingDeltaX) > 0.000001 || abs(event.deltaX) > 0.000001 {
super.scrollWheel(with: event)
return
}
if let cgEvent = event.cgEvent?.copy() {
cgEvent.setDoubleValueField(.scrollWheelEventDeltaAxis2, value: Double(event.scrollingDeltaY / 10))
cgEvent.setDoubleValueField(.scrollWheelEventDeltaAxis1, value: Double(0))
cgEvent.setDoubleValueField(.scrollWheelEventDeltaAxis3, value: Double(0))
cgEvent.setDoubleValueField(.mouseEventDeltaX, value: Double(0))
cgEvent.setDoubleValueField(.mouseEventDeltaY, value: Double(0))
// Once we flip the scrolling axis to X and set the rest to 0, we can just send the event the same as before. All the deceleration and such will get handled natively by the system!
if let nsEvent = NSEvent(cgEvent: cgEvent) {
super.scrollWheel(with: nsEvent)
} else {
super.scrollWheel(with: event)
}
} else {
super.scrollWheel(with: event)
}
}
}
}
extension View {
func captureVerticalScrollWheel() -> some View {
modifier(CaptureVerticalScrollWheelModifier())
}
}