如果鼠标移动太快,SwiftUIonHover不会注册鼠标离开元素

我在 SwiftUI 中制作了一些自定义滑块视图,它们根据悬停状态改变外观,但是如果鼠标移出太快(这实际上是移动光标的非常合理的速度),它会保持悬停状态,直到您重新-悬停并缓慢地重新离开组件。

有解决方案吗?悬停代码非常标准:

struct RulerSlider: View {
  @State var hovering = false

  var body: some View {
    GeometryReader { geometry in
      ZStack {
        // Ruler lines
        if hovering {
          Ruler()
        }
      }
      .onHover { hover in
        withAnimation(.easeOut(duration: 0.1)) {
          self.hovering = hover
        }
      }
    }
  }
}

这是问题的样子:

重现bug的示例代码:https :
//gist.github.com/rdev/ea0c53448e12835b29faa11fec8e0388

回答

我今天解决了这个问题,在一个空的 NSView 上有一个跟踪区域。这是在半复杂且快速刷新的网格视图中进行测试的,该视图以前具有与您想象的相同的行为。大约 75 个视图将此修饰符应用于此要点中的GIF 捕获,大多数视图彼此之间的边界为零。

呼叫站点的糖

import SwiftUI

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

可表示为空跟踪视图

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) }
        }
    }
}

  • Great! Works perfectly

以上是如果鼠标移动太快,SwiftUIonHover不会注册鼠标离开元素的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>