【问题标题】:SwiftUI: Two-finger swipe ( scroll ) gestureSwiftUI:两指滑动(滚动)手势
【发布时间】:2021-02-10 13:16:47
【问题描述】:

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

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

正如我所见,没有一个基本手势可以用于此:TapGesture - 不是; LongPressGesture - 不是; DragGesture - 不是;放大手势 - 不是; RotationGesture - 不是;

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

我至少需要看方向。


  • 这是 MacOS 项目
  • 顺便说一句,我无法在我的项目中使用 UI 类,我无法将项目重新制作为 Catlist

【问题讨论】:

标签: swift macos swiftui


【解决方案1】:

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

但是它只在NSView下可用,所以你需要使用NSRepresentableView将它集成到SwiftUI中。

这是一套完整的工作代码,使用滚轮滚动主图像。代码使用委托和回调将滚动事件沿链传递回 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()
  }
}

【讨论】:

    【解决方案2】:

    编辑:更正我的答案涵盖 Mac OS

    上下滚动是NSPanGestureRecognizer。它有一个 numberOfTouchesRequired 属性,如果需要,可以让它响应 2 个手指。

    Mac OS 没有滑动手势识别器。

    标准UISwipeGestureRecognizer 完全符合您的要求。只需将numberOfTouchesRequired 设置为 2。

    ...虽然我不确定移动 Safari 是否使用滑动手势。它可能是带有一些特殊编码的两指拖动。

    【讨论】:

    • 标记“macos”。目前没有 UI 类,只有 NS。 Google 没有向我展示 NSSwipeGestureRecognizer =((更准确地说是不可能将 UI 类连接到我的项目)
    • 哦,对不起。我已经习惯了每个人都在问 iOS,以至于我错过了。 UI 类是 iOS 特定的。
    【解决方案3】:
    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)
        }
    }
    

    【讨论】:

    • 这只是偶尔对我有用,这可能与我尝试将其附加到List 有关。每隔一段时间我都会得到一个打印输出,但遗憾的是,它不够可靠,无法附加逻辑。
    • 这足以在与信号系统一起使用的情况下附加逻辑
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多