macアプリでプリセット以外のカーソルを表示する方法を見つけるのに時間がかかったため、誰かの役に立てばと思い書き記します。
#1. 環境
macOS: 11.2.1 Big Sur
Swift: 5.3.2
Xcode: 12.4
#2. 全コード
TestView.swift
import Cocoa
class TestView: NSView {
private var trackingArea: NSTrackingArea?
override func updateTrackingAreas() {
super.updateTrackingAreas()
if self.trackingArea != nil {
self.removeTrackingArea(self.trackingArea!)
self.trackingArea = nil
}
let options: NSTrackingArea.Options = [.mouseMoved, .mouseEnteredAndExited, .activeAlways]
let rect = NSRect(origin: .zero, size: self.frame.size)
self.trackingArea = NSTrackingArea(rect: rect, options: options, owner: self, userInfo: nil)
self.addTrackingArea(self.trackingArea!)
}
override func mouseEntered(with event: NSEvent) {
let image = NSImage(named: "image_name")
let cursor = NSCursor(image: image!, hotSpot: .zero)
addCursorRect(self.bounds, cursor: cursor)
}
override func mouseExited(with event: NSEvent) {
NSCursor.arrow.set()
}
}
#3. 解説
対象のNSView自身と同じ大きさのトラッキング範囲を作成し、カーソルが出入りするとイベントを発火するようにします。
private var trackingArea: NSTrackingArea?
override func updateTrackingAreas() {
super.updateTrackingAreas()
if self.trackingArea != nil {
self.removeTrackingArea(self.trackingArea!)
self.trackingArea = nil
}
let options: NSTrackingArea.Options = [.mouseEnteredAndExited, .activeAlways]
let rect = NSRect(origin: .zero, size: self.frame.size)
self.trackingArea = NSTrackingArea(rect: rect, options: options, owner: self, userInfo: nil)
self.addTrackingArea(self.trackingArea!)
}
カーソルがトラッキング範囲に入ったタイミングでカスタムカーソルを表示します。プリセットのカーソルではset()
メソッドを使いますが、カスタムの場合addCursorRect()
を使う必要があります。
override func mouseEntered(with event: NSEvent) {
let image = NSImage(named: "image_name")
let cursor = NSCursor(image: image!, hotSpot: .zero)
addCursorRect(self.bounds, cursor: cursor)
}
上のコードの"hotSpot"は、カーソルの中でクリックを検知する座標を表します。矢印型のカーソルならばhotSpotを座標の左上に設定することで、見た目と動作を一致させることができます。
カーソルがトラッキング範囲を出たらデフォルトのカーソルに戻します。
override func mouseExited(with event: NSEvent) {
NSCursor.arrow.set()
}