SwiftUI:两指滑动(滚动)手势

And*_*rew 5 macos swift swiftui

我对 2 指滑动(滚动)手势感兴趣。

不是两指拖动,而是两指滑动(无需按下)。就像在 Safari 中用来向上和向下滚动一样。

据我所知,没有一个基本手势可以用于此目的:TapGesture - 不是;LongPressGesture - 不;DragGesture - 不;MagnificationGesture - 不;RotationGesture - 不;

有人知道如何做到这一点吗?

我至少需要看方向。


  • 这是MacOS项目
  • 顺便说一句,我无法在我的项目中使用 UI 类,我无法重新制作项目以进行目录列表

Tho*_*ell 6

充分尊重 @duncan-c 的答案,更有效的方法是使用NSResponderscrollWheel(with: NSEvent)来跟踪两根手指滚动(一根手指在 Apple 鼠标上)。

\n

然而它仅在 下可用NSView,因此您需要使用 将其集成到 SwiftUI 中NSRepresentableView

\n

这是一组完整的工作代码,使用滚轮滚动主图像。该代码使用委托和回调将滚动事件沿链传递回 SwiftUI:

\n
//\n//  ContentView.swift\n//  ScrollTest\n//\n//  Created by TR Solutions on 6/9/21.\n//\n\nimport SwiftUI\n\n/// How the view passes events back to the representable view.\nprotocol ScrollViewDelegateProtocol {\n  /// Informs the receiver that the mouse\xe2\x80\x99s scroll wheel has moved.\n  func scrollWheel(with event: NSEvent);\n}\n\n/// The AppKit view that captures scroll wheel events\nclass ScrollView: NSView {\n  /// Connection to the SwiftUI view that serves as the interface to our AppKit view.\n  var delegate: ScrollViewDelegateProtocol!\n  /// Let the responder chain know we will respond to events.\n  override var acceptsFirstResponder: Bool { true }\n  /// Informs the receiver that the mouse\xe2\x80\x99s scroll wheel has moved.\n  override func scrollWheel(with event: NSEvent) {\n    // pass the event on to the delegate\n    delegate.scrollWheel(with: event)\n  }\n}\n\n/// The SwiftUI view that serves as the interface to our AppKit view.\nstruct RepresentableScrollView: NSViewRepresentable, ScrollViewDelegateProtocol {\n  /// The AppKit view our SwiftUI view manages.\n  typealias NSViewType = ScrollView\n  \n  /// What the SwiftUI content wants us to do when the mouse's scroll wheel is moved.\n  private var scrollAction: ((NSEvent) -> Void)?\n  \n  /// Creates the view object and configures its initial state.\n  func makeNSView(context: Context) -> ScrollView {\n    // Make a scroll view and become its delegate\n    let view = ScrollView()\n    view.delegate = self;\n    return view\n  }\n  \n  /// Updates the state of the specified view with new information from SwiftUI.\n  func updateNSView(_ nsView: NSViewType, context: Context) {\n  }\n  \n  /// Informs the representable view  that the mouse\xe2\x80\x99s scroll wheel has moved.\n  func scrollWheel(with event: NSEvent) {\n    // Do whatever the content view wants\n    // us to do when the scroll wheel moved\n    if let scrollAction = scrollAction {\n      scrollAction(event)\n    }\n  }\n\n  /// Modifier that allows the content view to set an action in its context.\n  func onScroll(_ action: @escaping (NSEvent) -> Void) -> Self {\n    var newSelf = self\n    newSelf.scrollAction = action\n    return newSelf\n  }\n}\n\n/// Our SwiftUI content view that we want to be able to scroll.\nstruct ContentView: View {\n  /// The scroll offset -- when this value changes the view will be redrawn.\n  @State var offset: CGSize = CGSize(width: 0.0, height: 0.0)\n  /// The SwiftUI view that detects the scroll wheel movement.\n  var scrollView: some View {\n    // A view that will update the offset state variable\n    // when the scroll wheel moves\n    RepresentableScrollView()\n      .onScroll { event in\n        offset = CGSize(width: offset.width + event.deltaX, height: offset.height + event.deltaY)\n      }\n  }\n  /// The body of our view.\n  var body: some View {\n    // What we want to be able to scroll using offset(),\n    // overlaid (must be on top or it can't get the scroll event!)\n    // with the view that tracks the scroll wheel.\n    Image(systemName:"applelogo")\n      .scaleEffect(20.0)\n      .frame(width: 200, height: 200, alignment: .center)\n      .offset(offset)\n      .overlay(scrollView)\n  }\n}\n\nstruct ContentView_Previews: PreviewProvider {\n  static var previews: some View {\n    ContentView()\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


And*_*rew 2

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)
    }
}
Run Code Online (Sandbox Code Playgroud)