NSPressGestureRecognizer在模式ViewController中不起作用

时间:2019-12-24 15:22:15

标签: macos cocoa

我有一个NSViewController的子类,其中有一个视图,其中添加了一个NSPressGestureRecognizer。

如果我以StoryboardSegue中的显示样式显示ViewController,则NSPressGestureRecognizer可以工作。如果我在StoryboardSegue中将ViewController显示为模式样式,则NSPressGestureRecognizer无法正常工作。

有什么解决方法吗?

import Cocoa

class VC: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let view = NSView(frame: NSRect(x: 100, y: 100, width: 100, height: 100))
        view.wantsLayer = true
        view.layer?.backgroundColor = NSColor.systemBrown.cgColor
        let longPress = NSPressGestureRecognizer(target: self, action: #selector(longPress(_:)))
        view.addGestureRecognizer(longPress)
        self.view.addSubview(view)
    }

    @objc func longPress(_ sender:Any) {
        print("long")
    }
}

2 个答案:

答案 0 :(得分:1)

有趣。似乎正在发生的事情是在模态示例中,识别器状态从未从possible变为began

创建此...

class Recognizer: NSPressGestureRecognizer {

    override func mouseDown(with event: NSEvent) {
        super.mouseDown(with: event)
        self.state = .began
    }

}

...并将代码行修改为...

    let longPress = Recognizer(target: self, action: #selector(longPress(_:)))

...至少可以打印到“长”点。

警告:我建议您进行更多调查,因为我不知道是否会由于此覆盖而导致任何损坏。 (但指出了两种情况之间的差异。)

答案 1 :(得分:0)

由于@ phillip-mills的启发,我实现了“有效的” Recognizer

import Cocoa

class VC: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let view = NSView(frame: NSRect(x: 100, y: 100, width: 100, height: 100))
        view.wantsLayer = true
        view.layer?.backgroundColor = NSColor.systemBrown.cgColor
        let longPress = MyPressGestureRecognizer(target: self, action: #selector(longPress(_:)))
        view.addGestureRecognizer(longPress)

        let click = NSClickGestureRecognizer(target: self, action: #selector(click(_:)))
        view.addGestureRecognizer(click)

        self.view.addSubview(view)
    }

    @objc func click(_ sender:Any) {
        print("click")
    }

    @objc func longPress(_ sender:Any) {
        guard let gesture = sender as? NSGestureRecognizer else { return }

        switch gesture.state {
        case .ended:
            print("long")
        default:
            print(gesture.state)
        }
    }
}

class MyPressGestureRecognizer: NSPressGestureRecognizer {
    private weak var timer:Timer? = nil
    private var hasBegan = false
    private var hasCancelled = false

    override func mouseDown(with event: NSEvent) {
        super.mouseDown(with: event)

        timer = Timer.scheduledTimer(withTimeInterval: minimumPressDuration, repeats: false) { (timer) in
            defer {
                timer.invalidate()
            }

            DispatchQueue.main.async {
                self.state = .began
                self.hasBegan = true
            }
        }
    }

    override func mouseUp(with event: NSEvent) {
        if hasBegan {
            self.state = .ended
            self.hasBegan = false
        }

        super.mouseUp(with: event)
    }

    override func reset() {
        timer?.invalidate()
        super.reset()
    }
}

extension NSGestureRecognizer.State:CustomStringConvertible {
    public var description:String {
        switch self {
        case .possible:
            return "possible"
        case .began:
            return "began"
        case .changed:
            return "changed"
        case .ended:
            return "ended"
        case .cancelled:
            return "cancelled"
        case .failed:
            return "failed"
        @unknown default:
            return "default"
        }
    }
}
  

但是,我的自定义手势子类的工作原理与   苹果的,它不与模式视图控制器一起使用。我也是   大胆猜测苹果还使用了Timer   NSPressGestureRecognizer在模式视图控制器中不起作用。

这是工作代码。

import Cocoa

class VC: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let view = NSView(frame: NSRect(x: 100, y: 100, width: 100, height: 100))
        view.wantsLayer = true
        view.layer?.backgroundColor = NSColor.systemBrown.cgColor
        let longPress = MyPressGestureRecognizer(target: self, action: #selector(longPress(_:)))
        view.addGestureRecognizer(longPress)

        let click = NSClickGestureRecognizer(target: self, action: #selector(click(_:)))
        view.addGestureRecognizer(click)

        self.view.addSubview(view)
    }

    @objc func click(_ sender:Any) {
        print("click")
    }

    @objc func longPress(_ sender:Any) {
        guard let gesture = sender as? NSGestureRecognizer else { return }

        switch gesture.state {
        case .ended:
            print("long")
        default:
            print(gesture.state)
        }
    }
}

class MyPressGestureRecognizer: NSPressGestureRecognizer {
    private var hasBegan = false
    private var hasCancelled = false

    override func mouseDown(with event: NSEvent) {
        super.mouseDown(with: event)

        hasCancelled = false

        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(Int(minimumPressDuration * 1000))) {
            if !self.hasCancelled {
                self.state = .began
                self.hasBegan = true
            }
        }
    }

    override func mouseUp(with event: NSEvent) {
        if hasBegan {
            self.state = .ended
            self.hasBegan = false
        } else {
            self.hasCancelled = true
        }

        super.mouseUp(with: event)
    }

    override func reset() {
        super.reset()
    }
}

extension NSGestureRecognizer.State:CustomStringConvertible {
    public var description:String {
        switch self {
        case .possible:
            return "possible"
        case .began:
            return "began"
        case .changed:
            return "changed"
        case .ended:
            return "ended"
        case .cancelled:
            return "cancelled"
        case .failed:
            return "failed"
        @unknown default:
            return "default"
        }
    }
}

我将其他答案标记为已接受,以感谢对此答案的启发。

---------用DispatchSourceTimer更新---------------

class MyPressGestureRecognizer: NSPressGestureRecognizer {
    private var hasBegan = false
    private var timer:DispatchSourceTimer? = nil

    override func mouseDown(with event: NSEvent) {
        super.mouseDown(with: event)

        let timer = DispatchSource.makeTimerSource()
        timer.schedule(deadline: .now() + .milliseconds(Int(minimumPressDuration * 1000)))
        timer.setEventHandler {
            self.state = .began
            self.hasBegan = true
        }

        timer.activate()
        self.timer = timer
    }

    override func mouseUp(with event: NSEvent) {
        if hasBegan {
            self.state = .ended
            self.hasBegan = false
        } else {
            self.timer?.cancel()
        }

        super.mouseUp(with: event)
    }
}