Changing NSCursor for NSView above an NSTextView

后端 未结 3 735
借酒劲吻你
借酒劲吻你 2020-12-10 07:33

Found a similar question to mine(this), but my issues seems to be a bit more associated with view hierarchy.

I have a NSTextView, then as sibling views, several othe

相关标签:
3条回答
  • 2020-12-10 08:15

    A few important notes:

    • Be careful calling super on your mouseMoved or similar events, or the cursor might just get reset by the base class implementation.
    • Only reset your tracking area when the parent view size changes; if you try to do this by overriding layout() it's going to be happening all the time which is not great

    Here's an example class that you can just use as a base class in your storyboards.

    Swift 4 code:

    import Cocoa
    
    final class MouseTrackingTextView: NSTextView {
    
        // MARK: - Lifecycle
    
        override func awakeFromNib() {
            setupTrackingArea()
        }
    
        // MARK: - Resizing
    
        // Call this in your controller's `viewDidLayout`
        // so it only gets called when the view resizes
        func superviewResized() {
            resetTrackingArea()
        }
    
        // MARK: - Mouse Events
    
        override func resetCursorRects() {
            addCursorRect(bounds, cursor: cursorType)
        }
    
        override func mouseMoved(with event: NSEvent) {
            cursorType.set()
        }
    
        // MARK: - Private Properties
    
        private var currentTrackingArea: NSTrackingArea?
    
        private var cursorType: NSCursor {
            return isEditable ? .iBeam : .pointingHand
        }
    
        // MARK: - Private API
    
        private func setupTrackingArea() {
            let trackingArea = NSTrackingArea(rect: bounds,
                                              options: [.activeAlways, .mouseMoved],
                                              owner: self, userInfo: nil)
            currentTrackingArea = trackingArea
            addTrackingArea(trackingArea)
        }
    
        private func resetTrackingArea() {
            if let trackingArea = currentTrackingArea {
                removeTrackingArea(trackingArea)
            }
            setupTrackingArea()
        }
    
    }
    
    0 讨论(0)
  • 2020-12-10 08:19

    All you need is to subclass a custom view, override awakeFromNib method, add the custom tracking area for [.mouseMoved, .activeAlways] events: NSTrackingArea Info there. There is no need to override resetCursorRects and/or updateTrackingAreas. All you need is to override mouseMoved method and set the desired cursor there:

    Note about discardCursorRects method:

    From the docs

    You need never invoke this method directly

    Xcode 9 • Swift 4

    import Cocoa
    
    class CursorChangingView: NSView {
    
        override func awakeFromNib() {
    
            addTrackingArea(NSTrackingArea(rect: bounds, options: [.activeAlways, .mouseMoved], owner: self, userInfo: nil))
    
            wantsLayer = true
            layer?.backgroundColor = NSColor.cyan.cgColor
            layer?.borderColor = NSColor.black.cgColor
            layer?.borderWidth = 1
        }
    
        @objc override func mouseMoved(with theEvent: NSEvent) {
            NSCursor.pointingHand.set()
        }
    }
    

    Sample

    0 讨论(0)
  • 2020-12-10 08:26

    Thanks @Leo Dabus for your answer, but I managed to solve it, so I will post my answer too.

    In my case, for some reason, mouseEntered and mouseEntered did not work at all.

    So here is my code that finally got it to work:

    class CursorChangingView: NSView {
        let trackingArea: NSTrackingArea?
        func setupTracking() {
            if self.trackingArea == nil {
                self.trackingArea = NSTrackingArea(rect: self.bounds, options: NSTrackingAreaOptions.ActiveAlways | NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.CursorUpdate | NSTrackingAreaOptions.MouseEnteredAndExited | NSTrackingAreaOptions.ActiveInActiveApp, owner: self, userInfo: nil)
                self.addTrackingArea(self.trackingArea!)
            }
        }
    
        override func updateTrackingAreas() {
            self.trackingArea = NSTrackingArea(rect: self.bounds, options: NSTrackingAreaOptions.ActiveAlways | NSTrackingAreaOptions.CursorUpdate | NSTrackingAreaOptions.MouseEnteredAndExited | NSTrackingAreaOptions.ActiveInActiveApp, owner: self, userInfo: nil)
            self.addTrackingArea(self.trackingArea!)
        }
    
        override func resetCursorRects() {
            self.discardCursorRects()
            self.addCursorRect(self.bounds, cursor: NSCursor.arrowCursor())
        }
    
        override func mouseMoved(theEvent: NSEvent) {
            NSCursor.arrowCursor().set()
        }
    }
    

    It might be a little excessive, but worked, so will share this as my own solution.

    0 讨论(0)
提交回复
热议问题