Swiftui Onhover Doesn't Register Mouse Leaving the Element If Mouse Moves Too Fast

SwiftUI onHover doesn't register mouse leaving the element if mouse moves too fast

I resolved this issue today with a tracking area on an empty NSView. This is tested in a semi-complex and quickly refreshing grid view, which previously had the same behavior you pictured. About 75 views have this modifier applied in the GIF capture in this gist, most with zero border to each other.

Sugar for call site

import SwiftUI

extension View {
func whenHovered(_ mouseIsInside: @escaping (Bool) -> Void) -> some View {
modifier(MouseInsideModifier(mouseIsInside))
}
}

Representable with empty tracking view

struct MouseInsideModifier: ViewModifier {
let mouseIsInside: (Bool) -> Void

init(_ mouseIsInside: @escaping (Bool) -> Void) {
self.mouseIsInside = mouseIsInside
}

func body(content: Content) -> some View {
content.background(
GeometryReader { proxy in
Representable(mouseIsInside: mouseIsInside,
frame: proxy.frame(in: .global))
}
)
}

private struct Representable: NSViewRepresentable {
let mouseIsInside: (Bool) -> Void
let frame: NSRect

func makeCoordinator() -> Coordinator {
let coordinator = Coordinator()
coordinator.mouseIsInside = mouseIsInside
return coordinator
}

class Coordinator: NSResponder {
var mouseIsInside: ((Bool) -> Void)?

override func mouseEntered(with event: NSEvent) {
mouseIsInside?(true)
}

override func mouseExited(with event: NSEvent) {
mouseIsInside?(false)
}
}

func makeNSView(context: Context) -> NSView {
let view = NSView(frame: frame)

let options: NSTrackingArea.Options = [
.mouseEnteredAndExited,
.inVisibleRect,
.activeInKeyWindow
]

let trackingArea = NSTrackingArea(rect: frame,
options: options,
owner: context.coordinator,
userInfo: nil)

view.addTrackingArea(trackingArea)

return view
}

func updateNSView(_ nsView: NSView, context: Context) {}

static func dismantleNSView(_ nsView: NSView, coordinator: Coordinator) {
nsView.trackingAreas.forEach { nsView.removeTrackingArea($0) }
}
}
}

Change mouse cursor over inactive NSWindow

Now I finally found a solution that works. I don't know if this will bite me in the tail in the future but at least this seem to work when testing.

Thanks Wil for the example it got me half way there. But it was only when I finally combined it with resetCursorRects and also defined a cursor rect in each view with the specific cursor. This took me a long time to figure out and I don't know if the solution is optimal (suggestions of improvement are welcome)

Below is the full example that made it work for me in the end (self.cursor is an instance of the cursor)

- (void)viewWillMoveToWindow:(NSWindow *)newWindow {
NSTrackingArea *const trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:(NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect) owner:self userInfo:nil];
[self addTrackingArea:trackingArea];
[self.window invalidateCursorRectsForView:self];
}

- (void)resetCursorRects {
[super resetCursorRects];
[self addCursorRect:self.bounds cursor:self.cursor];
}

- (void)mouseEntered:(NSEvent *)theEvent {
[super mouseEntered:theEvent];
[self.cursor push];
}

- (void)mouseExited:(NSEvent *)theEvent {
[super mouseExited:theEvent];
[self.cursor pop];
}

Hover leaving Animation

I saw so a right answers above but here is another solution by JavaScript using mouseover and mouseleave events, but I notice you to use .hover, because it's simple.

div {
width: 50px;
height: 50px;
background: purple;
transition: all 1s;
}
    <div id="d"></div>
const d = document.getElementById("d");
d.addEventListener("mouseover",()=>{
d.style.boxShadow = "5px 5px 2px black";
})
d.addEventListener("mouseleave",()=>{
d.style.boxShadow = "0px 0px 0px";
})

Javascript and Database Connectivity

It is possible!

with the new html5 feature, js can connect through WebSql.
a live example : http://html5demos.com/database

the syntax is similar to all the other sql wrappers :

var db = openDatabase('mydb', '1.0', 'my first database', 2 * 1024 * 1024);
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE foo (id unique, text)');
});

it is currently supported by chrome, safari and opera

here's a tutorial : http://html5doctor.com/introducing-web-sql-databases/

How to prevent sticky hover effects for buttons on touch devices

Since this part of CSS Media Queries Level 4 has now been widely implemented since 2018, you can use this:

@media (hover: hover) {
button:hover {
background-color: blue;
}
}

Or in English: "If the browser supports proper/true/real/non-emulated hovering (e.g. has a mouse-like primary input device), then apply this style when buttons are hovered over."

For browsers that do not have this implemented (or didn't at the time of this original answer), I wrote a polyfill to deal with this. Using it, you can transform the above futuristic CSS into:

html.my-true-hover button:hover {
background-color: blue;
}

(A variation on the .no-touch technique) And then using some client-side JavaScript from the same polyfill that detects support for hovering, you can toggle the presence of the my-true-hover class accordingly:

$(document).on('mq4hsChange', function (e) {
$(document.documentElement).toggleClass('my-true-hover', e.trueHover);
});

Make div appear and change the whole html to be darker

HTML--

<a id="some-button" href="#">click me</a>
<div id="overlay-back"></div>
<div id="overlay"><span>YOUR HTML GOES HERE</span></div>

CSS--

html, body {
width : 100%;
height : 100%;
}
#overlay-back {
position : absolute;
top : 0;
left : 0;
width : 100%;
height : 100%;
background : #000;
opacity : 0.6;
filter : alpha(opacity=60);
z-index : 5;
display : none;
}

#overlay {
position : absolute;
top : 0;
left : 0;
width : 100%;
height : 100%;
z-index : 10;
display : none;
}

JS--

$('#some-button').on('click', function () {
$('#overlay, #overlay-back').fadeIn(500);
});

Then just add your youtube video embed code to the overlay div and style it appropriately to put it where you want on the page.

Here is a demo: http://jsfiddle.net/EtHbf/1/



Related Topics



Leave a reply



Submit