美文网首页
SwiftUI MacOS View的.OnHover 鼠标悬停

SwiftUI MacOS View的.OnHover 鼠标悬停

作者: O2Space_Xiu | 来源:发表于2022-12-13 11:41 被阅读0次

    在使用SwiftUI 开发 MacOS应用时,使用View的.OnHover来更新View样式时发现,当Window失去焦点,再次在该View处点击Window获得焦点,该View不会触发onHover。

    struct DemoView: View {
      @State var isHovering: Bool = false
      var body: some View {
        ZStack(alignment: .topLeading) {
          VStack(alignment: .center, spacing: 8){
              ...
          }
          .frame(width:400)
          .onHover { isHovering in
             self.isHovering = isHovering
          }
    
          if isHovering {
              // 添加视图
          }
        }
      }
    }
    

    优化方案

    加入鼠标监听视图层

    import AppKit
    import SwiftUI
    
    public class MouseMonitorNSView : NSView {
        
        var trackingArea : NSTrackingArea?
        public var onHover: ((Bool) -> Void)?
        
        public override func draw(_ dirtyRect: NSRect) {
            super.draw(dirtyRect)
            self.wantsLayer = true
            self.layer?.backgroundColor = NSColor.clear.cgColor
            
        }
            
        override public func updateTrackingAreas() {
            if let ta = trackingArea {
                self.removeTrackingArea(ta)
            }
            
            if !self.isHidden {
                let opt = (
                    NSTrackingArea.Options.mouseEnteredAndExited.rawValue
    //                | NSTrackingArea.Options.mouseMoved.rawValue
                    | NSTrackingArea.Options.activeAlways.rawValue
                )
                trackingArea = NSTrackingArea(rect: self.bounds, options: NSTrackingArea.Options(rawValue: opt), owner: self, userInfo: nil)
                self.addTrackingArea(trackingArea!)
            }
        }
        
        public override func mouseEntered(with event: NSEvent) {
    //        print("mouseEntered")
            if let hoverBlock = onHover {
                hoverBlock(true)
            }
        }
        
        public override func mouseExited(with event: NSEvent) {
    //        print("mouseExited")
            if let hoverBlock = onHover {
                hoverBlock(false)
            }
        }
        
    //    public override func mouseMoved(with event: NSEvent) {
    //        print("mouseMoved")
    //    }
    //
    }
    
    public struct MouseMonitorView : NSViewRepresentable {
        
        private let onHover: (Bool) -> Void
        
        public init(onHover: @escaping (Bool) -> Void){
            self.onHover = onHover
        }
        
        public func makeNSView(context: Context) -> MouseMonitorNSView {
            let view = MouseMonitorNSView()
            view.onHover = onHover
            return view
        }
        
        public func updateNSView(_ nsView: MouseMonitorNSView, context: Context) {
            nsView.onHover = onHover
        }
    }
    

    DemoView 改造后

    struct DemoView: View {
      @State var isHovering: Bool = false
      var body: some View {
        ZStack(alignment: .topLeading) {
          MouseMonitorView { isHovering in
            self.isHovering = isHovering
          }//此视图在window失去焦点亦能监听到鼠标事件,保持与VStack位置大小一致
          VStack(alignment: .center, spacing: 8){
              ...
          }
          .frame(width:400)
    //      .onHover { isHovering in
    //         self.isHovering = isHovering
    //      }
    
          if isHovering {
              // 添加视图
          }
        }
      }
    }
    

    此方案有个小小注意点:在window失去焦点时,视图亦能监听到鼠标进出事件

    后记

    是否有更优雅的方案,评论区留言

    相关文章

      网友评论

          本文标题:SwiftUI MacOS View的.OnHover 鼠标悬停

          本文链接:https://www.haomeiwen.com/subject/pbjhqdtx.html