Skip to content

Instantly share code, notes, and snippets.

@Amzd
Last active February 9, 2024 17:34
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Amzd/cb8ba40625aeb6a015101d357acaad88 to your computer and use it in GitHub Desktop.
Save Amzd/cb8ba40625aeb6a015101d357acaad88 to your computer and use it in GitHub Desktop.
Set the cursor that is displayed when hovering a View. (macOS, SwiftUI)
import SwiftUI
extension View {
/// https://stackoverflow.com/a/61985678/3393964
public func cursor(_ cursor: NSCursor) -> some View {
self.onHover { inside in
if inside {
cursor.push()
} else {
NSCursor.pop()
}
}
}
}
@Amzd
Copy link
Author

Amzd commented Nov 9, 2020

Honestly can't believe Button doesn't do this by default.

@lucasfeijo
Copy link

For me, the correct cursor shows briefly but then disappears.

@lucasfeijo
Copy link

lucasfeijo commented Feb 13, 2023

A year later I'm back here with code that works:

extension View {
  public func cursor(_ cursor: NSCursor) -> some View {
    if #available(macOS 13.0, *) {
      return self.onContinuousHover { phase in
        switch phase {
        case .active(let p):
          cursor.push()
        case .ended:
          NSCursor.pop()
        }
      }
    } else {
      return self.onHover { inside in
        if inside {
          cursor.push()
        } else {
          NSCursor.pop()
        }
      }
    }
  }
}

@Amzd
Copy link
Author

Amzd commented Feb 14, 2023

Nice! Thank you for updating here as I will fix this in my projects too :D @lucasfeijo

@humblehacker
Copy link

Like its name says, onContinuousHover calls its callback continuously, resulting in many cursor pushes and only a single pop at the end. The result is that when the cursor leaves the view, the original cursor is not restored. So here's my modification:

extension View {
    public func cursor(_ cursor: NSCursor) -> some View {
        if #available(macOS 13.0, *) {
            return self.onContinuousHover { phase in
                switch phase {
                case .active(_):
                    guard NSCursor.current != cursor else { return }
                    cursor.push()
                case .ended:
                    NSCursor.pop()
                }
            }
        } else {
            return self.onHover { inside in
                if inside {
                    cursor.push()
                } else {
                    NSCursor.pop()
                }
            }
        }
    }
}

@lucasfeijo
Copy link

@humblehacker We'll get it right eventually, meanwhile @apple has been ignoring this bug...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment