Skip to content

Instantly share code, notes, and snippets.

@edvinassabaliauskas
Last active August 16, 2021 07:06
Show Gist options
  • Save edvinassabaliauskas/e14d8dfe7bcb866233ee3a50d05069a7 to your computer and use it in GitHub Desktop.
Save edvinassabaliauskas/e14d8dfe7bcb866233ee3a50d05069a7 to your computer and use it in GitHub Desktop.
Implements 2 finger swipe using scroll event tracking
class SwipeableViewController: NSViewController {
private var isHandlingScrollEvent = false
private var swipeLeftExecuted = false
private var swipeRightExecuted = false
private var scrollDeltaX: CGFloat = 0
override func wantsScrollEventsForSwipeTracking(on axis: NSEvent.GestureAxis) -> Bool {
return axis == .horizontal
}
/// Informs the receiver that the user has begun a swipe gesture.
///
/// NOTE!
/// Works with 3 finger swipe gesture (strange, I know).
///
/// Works only when System preferences -> Trackpad -> More Gestures, preferences are set to the following:
/// 1. Swipe between pages:
/// - Swipe with two or three fingers, or
/// - Swipe with three fingers
/// 2. Swipe between full-screen apps:
/// - Swipe left or right with four fingers
override func swipe(with event: NSEvent) {
swipe(deltaX: event.deltaX)
}
/// This implements 2 finger swipe using scroll event tracking.
override func scrollWheel(with event: NSEvent) {
if !NSEvent.isSwipeTrackingFromScrollEventsEnabled {
super.scrollWheel(with: event)
return
}
switch event.phase {
case .began:
isHandlingScrollEvent = true
swipeLeftExecuted = false
swipeRightExecuted = false
scrollDeltaX = 0
case .changed:
guard isHandlingScrollEvent else { break }
let directionChanged = scrollDeltaX.sign != event.scrollingDeltaX.sign
guard !directionChanged else {
scrollDeltaX = event.scrollingDeltaX
break
}
scrollDeltaX += event.scrollingDeltaX
// throttle
guard abs(scrollDeltaX) > 50 else { break }
let flippedScrollDelta = scrollDeltaX * -1
let swipedLeft = flippedScrollDelta > 0
switch (swipedLeft, swipeLeftExecuted, swipeRightExecuted) {
case (true, false, _): // swiped left
swipeLeftExecuted = true
swipeRightExecuted = false // allow swipe back (right)
case (false, _, false): // swiped right
swipeLeftExecuted = false // allow swipe back (left)
swipeRightExecuted = true
default:
super.scrollWheel(with: event)
return
}
swipe(deltaX: flippedScrollDelta)
return
case .ended,
.cancelled,
.mayBegin:
isHandlingScrollEvent = false
default:
break
}
super.scrollWheel(with: event)
}
private func swipe(deltaX: CGFloat) {
guard deltaX != 0 else { return }
let swipedLeft = deltaX > 0
if swipedLeft {
// swipe left action
} else {
// swipe right action
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment