【问题标题】:Finding click location in SwiftUI on MacOS在 MacOS 上的 SwiftUI 中查找点击位置
【发布时间】:2020-11-28 05:46:24
【问题描述】:

我尝试使用 NSViewRepresentable 而不是 UIViewRepresentable 将我在 iOS 应用程序上使用的解决方案调整到 macOS。

下面是我的“可点击视图”。我的问题是,当我尝试使用此视图时,我收到错误 Cannot find "TappableView" in scope

谢谢。

(使用 Xcode 版本 12.0 beta 4)

import Foundation
import SwiftUI

struct TappableView: NSViewRepresentable {
    
    var tappedCallback: ((CGPoint, Int) -> Void)
        
    func makeNSView(context: NSViewRepresentableContext<TappableView>) -> NSView {
        let v = UIView(frame: .zero)
        let gesture = NSClickGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.tapped))
        gesture.numberOfTapsRequired = 1
        let gesture2 = NSClickGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.doubleTapped))
        gesture2.numberOfTapsRequired = 2
        gesture.require(toFail: gesture2)
        v.addGestureRecognizer(gesture)
        v.addGestureRecognizer(gesture2)
        return v
    }
    
    class Coordinator: NSObject {
        var tappedCallback: ((CGPoint, Int) -> Void)
        init(tappedCallback: @escaping ((CGPoint, Int) -> Void)) {
            self.tappedCallback = tappedCallback
        }
        @objc func tapped(gesture:NSClickGestureRecognizer) {
            let point = gesture.location(in: gesture.view)
            self.tappedCallback(point, 1)
        }
        @objc func doubleTapped(gesture:NSClickGestureRecognizer) {
            let point = gesture.location(in: gesture.view)
            self.tappedCallback(point, 2)
        }
    }
    
    func makeCoordinator() -> TappableView.Coordinator {
        return Coordinator(tappedCallback:self.tappedCallback)
    }
    
    func updateNSView(_ nsView: NSView, context: NSViewRepresentableContext<TappableView>) {
    }
        
}

【问题讨论】:

    标签: macos swiftui nsviewrepresentable


    【解决方案1】:

    更好的解决方案是使用 SwiftUI 手势组合而不是 NSViewRepresentable 解决方案,因为它可以更好地与 SwiftUI API 集成。提出的其他解决方案的问题是,如果您想编写复杂的手势,则不能使用.highPriorityGesture() 或任何其他Gesture API。

    这是一个有效的实现:

    struct ClickGesture: Gesture {
        let count: Int
        let coordinateSpace: CoordinateSpace
        
        typealias Value = SimultaneousGesture<TapGesture, DragGesture>.Value
        
        init(count: Int = 1, coordinateSpace: CoordinateSpace = .local) {
            precondition(count > 0, "Count must be greater than or equal to 1.")
            self.count = count
            self.coordinateSpace = coordinateSpace
        }
        
        var body: SimultaneousGesture<TapGesture, DragGesture> {
            SimultaneousGesture(
                TapGesture(count: count),
                DragGesture(minimumDistance: 0, coordinateSpace: coordinateSpace)
            )
        }
        
        func onEnded(perform action: @escaping (CGPoint) -> Void) -> some Gesture {
            ClickGesture(count: count, coordinateSpace: coordinateSpace)
                .onEnded { (value: Value) -> Void in
                    guard value.first != nil else { return }
                    guard let location = value.second?.startLocation else { return }
                    guard let endLocation = value.second?.location else { return }
                    guard ((location.x-1)...(location.x+1)).contains(endLocation.x),
                          ((location.y-1)...(location.y+1)).contains(endLocation.y) else {
                        return
                    }
                    
                    action(location)
                }
        }
    }
    
    extension View {
        func onClickGesture(
            count: Int,
            coordinateSpace: CoordinateSpace = .local,
            perform action: @escaping (CGPoint) -> Void
        ) -> some View {
            gesture(ClickGesture(count: count, coordinateSpace: coordinateSpace)
                .onEnded(perform: action)
            )
        }
        
        func onClickGesture(
            count: Int,
            perform action: @escaping (CGPoint) -> Void
        ) -> some View {
            onClickGesture(count: count, coordinateSpace: .local, perform: action)
        }
        
        func onClickGesture(
            perform action: @escaping (CGPoint) -> Void
        ) -> some View {
            onClickGesture(count: 1, coordinateSpace: .local, perform: action)
        }
    }
    

    您可以像任何其他 SwiftUI 手势一样通过便捷的 API .onClickGesture(count:perform:) 使用它。

    【讨论】:

      【解决方案2】:

      这是工作变体

      struct TappableView: NSViewRepresentable {
      
          var tappedCallback: ((CGPoint, Int) -> Void)
      
          func makeNSView(context: NSViewRepresentableContext<TappableView>) -> NSView {
              let v = NSView(frame: .zero)
              context.coordinator.configure(view: v)
              return v
          }
      
          class Coordinator: NSObject, NSGestureRecognizerDelegate {
              var tappedCallback: ((CGPoint, Int) -> Void)
              private var gesture: NSClickGestureRecognizer!
              private var gesture2: NSClickGestureRecognizer!
      
              init(tappedCallback: @escaping ((CGPoint, Int) -> Void)) {
                  self.tappedCallback = tappedCallback
              }
              func configure(view: NSView) {
                  gesture = NSClickGestureRecognizer(target: self, action: #selector(Coordinator.tapped))
                  gesture.delegate = self
                  gesture.numberOfClicksRequired = 1
                  gesture2 = NSClickGestureRecognizer(target: self, action: #selector(Coordinator.doubleTapped))
                  gesture2.delegate = self
                  gesture2.numberOfClicksRequired = 2
                  view.addGestureRecognizer(gesture)
                  view.addGestureRecognizer(gesture2)
              }
              @objc func tapped(gesture:NSClickGestureRecognizer) {
                  let point = gesture.location(in: gesture.view)
                  self.tappedCallback(point, 1)
              }
              @objc func doubleTapped(gesture:NSClickGestureRecognizer) {
                  let point = gesture.location(in: gesture.view)
                  self.tappedCallback(point, 2)
              }
      
              func gestureRecognizer(_ gestureRecognizer: NSGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: NSGestureRecognizer) -> Bool {
                  return gestureRecognizer === gesture && otherGestureRecognizer === gesture2
              }
          }
      
          func makeCoordinator() -> TappableView.Coordinator {
              return Coordinator(tappedCallback:self.tappedCallback)
          }
      
          func updateNSView(_ nsView: NSView, context: NSViewRepresentableContext<TappableView>) {
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 2020-06-07
        • 2022-01-23
        • 2022-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-24
        相关资源
        最近更新 更多