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
A few important notes:
super
on your mouseMoved
or similar events, or the cursor might just get reset by the base class implementation.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()
}
}
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
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.