デフォルトでは無効化されているイベント
NSViewのmouseEnteredやmouseExitedはデフォルトでは無効化されており、有効化させるには
1.NSViewのupdateTrackingAreasをoverrideし、
2.NSView自身のサイズと同じ領域でaddTrackingAreaメソッドを用いてNSTrackingAreaを指定する
という手順をふむ必要があります。
override func updateTrackingAreas() {
//すでに指定済みのtrackingAreaを一旦解除しておく
if !trackingAreas.isEmpty {
for area in trackingAreas {
removeTrackingArea(area)
}
}
//自分のsizeが0ならば何もしない
if bounds.size.width == 0 || bounds.size.height == 0 { return }
//トラッキングオプションを指定(Enter / Exit と一緒に Moved も対象にしてます)
let options: NSTrackingAreaOptions = [
.mouseEnteredAndExited,
.mouseMoved,
.activeInActiveApp,
.assumeInside
]
//NSTrackingAreaのインスタンスを作り、addTrackingAreaで指定
let area = NSTrackingArea(rect: bounds, options: options, owner: self, userInfo: nil)
addTrackingArea(area)
}
コピペだと芸がありませんし、SuperClassに機能を持たせてもいいのですが、NSButtonなどを拡張したい場合も考えて、ここはひとつProtocol Extensionで実装をまとめてしまいましょう。
NSTrackingAreaを有効化するメソッドをプロトコルに実装するよ
//空のプロトコルを定義
protocol MouseTrackableView {}
//MouseEnter や Exit Moveのイベントを有効化させる機能を持ったプロトコル拡張でNSViewにのみ有効にする
extension MouseTrackableView where Self: NSView {
/*
現在のrectに合わせてTrackingAreaを更新するメソッド
このプロトコル拡張では複数のTrackingAreaを保持することは許容しておらずに
renewTrackAreaを呼び出したタイミングで、必ず自身のRectに合わせたTrackingAreaを
一つだけ保持することになる
*/
func renewTrackArea() {
//すでに指定済みのtrackingAreaを一旦解除しておく
if !trackingAreas.isEmpty {
for area in trackingAreas {
removeTrackingArea(area)
}
}
//自分のsizeが0ならば何もしない
if bounds.size.width == 0 || bounds.size.height == 0 { return }
//トラッキングオプションを指定(Enter / Exit と一緒に Moved も対象にしてます)
let options: NSTrackingAreaOptions = [
.mouseEnteredAndExited,
.mouseMoved,
.activeInActiveApp,
.assumeInside
]
//NSTrackingAreaのインスタンスを作り、addTrackingAreaで指定
let area = NSTrackingArea(rect: bounds, options: options, owner: self, userInfo: nil)
addTrackingArea(area)
}
}
使い方
後は、updateTrackingAreasをoverrideします
class MouseEventView: NSView, MouseTrackableView {
override func updateTrackingAreas() {
renewTrackArea()
}
override func mouseEntered(with theEvent: NSEvent) {
NSLog("mouseEntered")
}
override func mouseExited(with theEvent: NSEvent) {
NSLog("mouseExited")
}
}