【问题标题】:Changing NSCursor for NSView above an NSTextView在 NSTextView 上方更改 NSView 的 NSCursor
【发布时间】:2018-05-05 23:30:31
【问题描述】:

发现了一个和mine(this)类似的问题, 但我的问题似乎与视图层次结构更相关。

我有一个 NSTextView,然后作为兄弟视图,在它上面还有几个其他 NSView。

与上面链接的问题类似,我设置了一个跟踪区域,并像这样应用光标:

class CursorChangingView: NSView {
    override func updateTrackingAreas() {
        let trackingArea = NSTrackingArea(rect: 
    }

    override func cursorUpdate(event: NSEvent) {
        NSCursor.arrowCursor().set()
    }
}

它在悬停时似乎确实有效,但立即返回IBeam Cursor,这是此 CursorChangingView 下 NSTextViews 的默认光标。

这是在将鼠标悬停在某个 NSView 上时应用更改光标的正确方法,并且它下面的 NSTextView 是否覆盖了我的覆盖?

【问题讨论】:

    标签: macos swift cocoa


    【解决方案1】:

    您只需要子类化自定义视图,覆盖 awakeFromNib 方法,为 [.mouseMoved, .activeAlways] 事件添加自定义跟踪区域:NSTrackingArea Info 那里。无需覆盖resetCursorRects 和/或updateTrackingAreas。您只需要覆盖 mouseMoved 方法并在那里设置所需的光标:

    注意discardCursorRects方法:

    来自文档

    你永远不需要直接调用这个方法

    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

    【讨论】:

    • 对我不起作用。如果自定义视图下方的文本字段有第一响应者,光标仍会变回 IBeam
    【解决方案2】:

    感谢@Leo Dabus 的回答, 但我设法解决了它,所以我也会发布我的答案。

    就我而言,出于某种原因,mouseEnteredmouseEntered 根本不起作用。

    所以这是我的代码,终于让它工作了:

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

    这可能有点过分,但很有效,所以将其作为我自己的解决方案分享。

    【讨论】:

    • 就我而言,当CursorChangingView 高于NSTextView 时,它仍然会闪烁。你是怎么摆脱它的?
    • 另外,您第二次创建跟踪时,您将不再跟踪 .mouseMoved
    【解决方案3】:

    一些重要说明:

    • 在您的 mouseMoved 或类似事件上调用 super 时要小心,否则游标可能会被基类实现重置。
    • 仅在父视图大小更改时重置您的跟踪区域;如果您尝试通过覆盖 layout() 来做到这一点,它将会一直发生,这不是很好

    这是一个示例类,您可以将其用作情节提要中的基类。

    Swift 4 代码:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-13
      • 2013-01-16
      • 2011-07-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多