I'm interested in 2-finger swipe ( scroll ) gesture.
Not two-finger drag, but 2-finger swipe (without press). Like used in Safari to scroll up and down.
As I see noone of basic gestures will work for this: TapGesture - is not; LongPressGesture - not; DragGesture - not; MagnificationGesture - not; RotationGesture - not;
Have anyone some ideas how to do this?
I need at least direction to look at.
With due respect to @duncan-c 's answer, the more effective way is to use the NSResponder
's scrollWheel(with: NSEvent)
mechanism to track two-finger scrolling (one finger on the Apple Mouse).
However it's only available under NSView
, so you need to integrate it into SwiftUI using NSRepresentableView
.
Here is a complete set of working code that scrolls the main image using the scroll wheel. The code uses delegates and callbacks to pass the scroll event back up the chain into SwiftUI:
//
// ContentView.swift
// ScrollTest
//
// Created by TR Solutions on 6/9/21.
//
import SwiftUI
/// How the view passes events back to the representable view.
protocol ScrollViewDelegateProtocol {
/// Informs the receiver that the mouse’s scroll wheel has moved.
func scrollWheel(with event: NSEvent);
}
/// The AppKit view that captures scroll wheel events
class ScrollView: NSView {
/// Connection to the SwiftUI view that serves as the interface to our AppKit view.
var delegate: ScrollViewDelegateProtocol!
/// Let the responder chain know we will respond to events.
override var acceptsFirstResponder: Bool { true }
/// Informs the receiver that the mouse’s scroll wheel has moved.
override func scrollWheel(with event: NSEvent) {
// pass the event on to the delegate
delegate.scrollWheel(with: event)
}
}
/// The SwiftUI view that serves as the interface to our AppKit view.
struct RepresentableScrollView: NSViewRepresentable, ScrollViewDelegateProtocol {
/// The AppKit view our SwiftUI view manages.
typealias NSViewType = ScrollView
/// What the SwiftUI content wants us to do when the mouse's scroll wheel is moved.
private var scrollAction: ((NSEvent) -> Void)?
/// Creates the view object and configures its initial state.
func makeNSView(context: Context) -> ScrollView {
// Make a scroll view and become its delegate
let view = ScrollView()
view.delegate = self;
return view
}
/// Updates the state of the specified view with new information from SwiftUI.
func updateNSView(_ nsView: NSViewType, context: Context) {
}
/// Informs the representable view that the mouse’s scroll wheel has moved.
func scrollWheel(with event: NSEvent) {
// Do whatever the content view wants
// us to do when the scroll wheel moved
if let scrollAction = scrollAction {
scrollAction(event)
}
}
/// Modifier that allows the content view to set an action in its context.
func onScroll(_ action: @escaping (NSEvent) -> Void) -> Self {
var newSelf = self
newSelf.scrollAction = action
return newSelf
}
}
/// Our SwiftUI content view that we want to be able to scroll.
struct ContentView: View {
/// The scroll offset -- when this value changes the view will be redrawn.
@State var offset: CGSize = CGSize(width: 0.0, height: 0.0)
/// The SwiftUI view that detects the scroll wheel movement.
var scrollView: some View {
// A view that will update the offset state variable
// when the scroll wheel moves
RepresentableScrollView()
.onScroll { event in
offset = CGSize(width: offset.width + event.deltaX, height: offset.height + event.deltaY)
}
}
/// The body of our view.
var body: some View {
// What we want to be able to scroll using offset(),
// overlaid (must be on top or it can't get the scroll event!)
// with the view that tracks the scroll wheel.
Image(systemName:"applelogo")
.scaleEffect(20.0)
.frame(width: 200, height: 200, alignment: .center)
.offset(offset)
.overlay(scrollView)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Scrolling up and down is a NSPanGestureRecognizer
. That has a numberOfTouchesRequired
property that lets you make it respond to 2 fingers if desired.
Mac OS does not have a swipe gesture recognizer.
The standard UISwipeGestureRecognizer
does exactly what you want. Just set numberOfTouchesRequired
to 2.
...Although I'm not sure mobile Safari uses swipe gestures. It might be a 2-finger drag with some special coding.
import Combine
@main
struct MyApp: App {
@State var subs = Set<AnyCancellable>() // Cancel onDisappear
@SceneBuilder
var body: some Scene {
WindowGroup {
SomeWindowView()
/////////////
// HERE!!!!!
/////////////
.onAppear { trackScrollWheel() }
}
}
}
/////////////
// HERE!!!!!
/////////////
extension MyApp {
func trackScrollWheel() {
NSApp.publisher(for: \.currentEvent)
.filter { event in event?.type == .scrollWheel }
.throttle(for: .milliseconds(200),
scheduler: DispatchQueue.main,
latest: true)
.sink {
if let event = $0 {
if event.deltaX > 0 { print("right") }
if event.deltaX < 0 { print("left") }
if event.deltaY > 0 { print("down") }
if event.deltaY < 0 { print("up") }
}
}
.store(in: &subs)
}
}
I'm interested in 2-finger swipe ( scroll ) gesture.
Not two-finger drag, but 2-finger swipe (without press). Like used in Safari to scroll up and down.
As I see noone of basic gestures will work for this: TapGesture - is not; LongPressGesture - not; DragGesture - not; MagnificationGesture - not; RotationGesture - not;
Have anyone some ideas how to do this?
I need at least direction to look at.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.