1

我尝试使用 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>) {
    }
        
}

4

2 回答 2

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-08-08T04:42:43.603 回答
1

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

这是一个有效的实现:

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)
    }
}

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

于 2021-03-08T11:30:09.483 回答