23

Swift newbie here.

I've been having trouble with a task that should be trivial. All I want to do is get the x,y coordinates of the mouse cursor on-demand. I would prefer not to wait for a mouse movement event to fire before I can grab the pointer's coords.

Would appreciate any help!

MattY
  • 527
  • 2
  • 4
  • 9

2 Answers2

42

You should take a look at NSEvent method mouseLocation

edit/update: Xcode 11 • Swift 5.1

If you would like to monitor events on any window when your app is active, you can add a LocalMonitorForEvents matching mouseMoved mask and if it is not active a GlobalMonitorForEvents. Note that you need set to your window property acceptsMouseMovedEvents to true

import Cocoa

class ViewController: NSViewController {
    lazy var window: NSWindow = self.view.window!
    var mouseLocation: NSPoint { NSEvent.mouseLocation }
    var location: NSPoint { window.mouseLocationOutsideOfEventStream }
    override func viewDidLoad() {
        super.viewDidLoad()
        NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
            print("mouseLocation:", String(format: "%.1f, %.1f", self.mouseLocation.x, self.mouseLocation.y))
            print("windowLocation:", String(format: "%.1f, %.1f", self.location.x, self.location.y))
            return $0
        }
        NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved]) { _ in
            print(String(format: "%.0f, %.0f", self.mouseLocation.x, self.mouseLocation.y))
        }
    }
    override func viewWillAppear() {
        super.viewWillAppear()
        window.acceptsMouseMovedEvents = true
    }
}

Sample project

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • 2
    Thanks for the excellent answer. Works perfectly. NSEvent is a goldmine. :) – MattY Aug 12 '15 at 06:57
  • 1
    Great solution @Leo! However, I couldn’t find out how to receive the `mouseLocation` if its position is outside of my custom window. I want to achieve some kind of color picker and want to draw a custom view besides the cursor. Would be great if you could lead me into the right direction! – ixany Jan 21 '17 at 09:59
  • 1
    @ixany sorry for the delay I just saw your question. You can use `NSEvent.addLocalMonitorForEvents(matching: .mouseMoved) { print(NSEvent.mouseLocation()) return $0 }` If you would like to monitor it when your app is not active just use global instead of local – Leo Dabus Mar 22 '17 at 12:34
  • Great answer, what would be the way to do it if I just want to track the mouse inside an NSView? (I'm new to Mac development as well..) – Nils Sep 16 '17 at 11:07
  • @Nils You can add a window property to your view controller using the lazy keyword as follow `lazy var window: NSWindow = self.view.window!` and then access the NSWindow property mouseLocationOutsideOfEventStream `window.mouseLocationOutsideOfEventStream`. Check the last edit how you can monitor the mouse location and its position in the window. – Leo Dabus Oct 10 '17 at 02:01
  • Use **_self.view.window?.acceptsMouseMovedEvents = true_** Otherwise, declaring global **_window_ forced variable** cause retain cycle. – M Afham Mar 28 '20 at 08:46
  • @MAfham I have not declared window as a global var. It is declared as a property of the view controller. It will live as long as the view controller does. Anyway `self.view.window?.acceptsMouseMovedEvents = true` is ok as long as it is not used inside viewDidLoad where the window property is nil – Leo Dabus Apr 20 '20 at 18:50
3

You can get the current mouse location in this way:

  • Declare this in your view controller class:

    var mouseLocation: NSPoint? { self.view.window?.mouseLocationOutsideOfEventStream }
    
  • Then, you can get the current mouse location and convert in your desired view coordinates:

    if let currentMouseLocation = self.mouseLocation{
    
         let pointInTargetView = self.**targetView**.convert(currentMouseLocation, from: self.view)
    
    }
    
M Afham
  • 834
  • 10
  • 15