Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2020 SwiftUI & UI Frameworks

WWDC20 · 24 min · SwiftUI & UI Frameworks

Handle trackpad and mouse input

Provide a more versatile experience when you optimize your iPad or Mac Catalyst app for indirect input from trackpads and mice. Discover how to make your app responsive to new events from these devices. Learn how to work with pointer movement, enable pointer locking, handle scroll input and trackpad gestures, and accept or reject events on your gesture recognizers. We’ll also show you how to implement advanced features like changing gesture behaviors with keyboard modifiers or pointing device buttons to delight pro users and bring a richer experience to your app. To learn more about pointer-based interactions and to get the most out of this session, we recommend watching “Build for the iPadOS pointer,” “Bring keyboard and mouse gaming to iPad,” and “Support hardware keyboards in your app.”

Watch at developer.apple.com ↗

Transcript all transcripts

Code shown on screen · 9 snippets

UIHoverGestureRecognizer swift · at 1:49 ↗
let controlsHover = UIHoverGestureRecognizer(target: self, action: #selector(handleHover))

@objc func handleHover(_ recognizer: UIHoverGestureRecognizer) {
    switch recognizer.state {
    case .began:
        // Pointer entered our view - show controls
        self.showsPlaybackControls = true
    case .ended:
        // Pointer exited our view - hide controls
        self.showsPlaybackControls = false
    default:
        break
    }
}
prefersPointerLocked swift · at 5:33 ↗
class GameViewController: UIViewController {
    
    var shouldLockPointer: Bool = true
    
    override var prefersPointerLocked: Bool {
        return self.shouldLockPointer
    }
    
    func disablePointerLock() {
        self.shouldLockPointer = false
        self.setNeedsUpdateOfPrefersPointerLocked()
    }
}
UIPointerLockState.isLocked swift · at 5:53 ↗
if let pointerLockState = self.window.windowScene?.pointerLockState {
    self.observer = notificationCenter.addObserver(forName: UIPointerLockState.didChangeNotification,
                                                   object: pointerLockState,
                                                   queue: OperationQueue.main) { (note) in
        guard let lockState = note.object as? UIPointerLockState else { return }
        gameEngine.performExpensiveOperationWhile(lockState.isLocked)
    }
}
UIPanGestureRecognizer.allowedScrollTypesMask swift · at 9:54 ↗
// Enable scroll input for touch surface devices
    
self.drawerPan.allowedScrollTypesMask = [.continuous]
        

// Enable scroll input for scroll wheel devices as well

self.pullToRefreshPan.allowedScrollTypesMask = [.all]
Requiring a 3rd mouse button click swift · at 14:48 ↗
self.thirdMouseButtonTap.buttonMaskRequired = .button(3)
Changing response for .alternate keyboard modifier swift · at 15:07 ↗
func handleHover(_ recognizer: UIHoverGestureRecognizer) {
        
    // Show chapter controls if alt is pressed
    let showChapterControls = recognizer.modifierFlags.contains(.alternate)
        
    // ...
}
Only handle secondary clicks swift · at 16:38 ↗
class SecondaryClickGesture: UIGestureRecognizer {
    
    override func shouldReceive(_ event: UIEvent) -> Bool {
        // Must look at the event’s mask, not the gesture’s 
        return event.buttonMask == .secondary
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        // Touch handling code ...
    }
}
Only handle secondary clicks or control clicks swift · at 17:36 ↗
class SecondaryClickGesture: UIGestureRecognizer {
    
    override func shouldReceive(_ event: UIEvent) -> Bool {
        // Must look at the event’s properties, not the gesture’s
        let secondaryClick = event.buttonMask == .secondary

        let controlClick = event.buttonMask == .primary && event.modifierFlags == .control 

        return secondaryClick || controlClick
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        // Touch handling code ...
    }
}
Only receive hover events with the .alternate modifier pressed swift · at 18:10 ↗
let ccHover = UIHoverGestureRecognizer(target: self, 
                                       action: #selector(handleClosedCaptionHover))

ccHover.delegate = self
    
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, 
                       shouldReceive event: UIEvent) -> Bool {

    if gestureRecognizer == self.closedCaptionHover {
        return event.modifierFlags.contains(.alternate)
    }

    return true
}