我希望我的NSCursor在我所有的应用程序中都成为pointingHand.

我写了下面的代码,但我不认为这是可行的,因为一开始我试图通过一个按钮的操作来改变NSCursor.它可以用,但我一把鼠标移到NSCursor,它的形状就变成了MacOS的默认形状.然后,我try 了以下代码:

 struct ContentView: View {
    var body: some View {
        
        ZStack {
            Color.blue
            Text("Hello, world!")
        }
        .onHover(perform: { value in
            NSCursor.pointingHand.push()
        })

    }
}

我不喜欢这个代码,因为onHover几乎每秒都会更新NSCursor,我认为onHover一直在与MacOS竞争,将NSCursor更改为pointingHand.在这种级别的编码中,这意味着即使用计时器也可以取代onHover,并可以做同样的事情.正因为如此,我确信使用onHover并不是最适合我的目标.

如何在不使用onHover修改器的情况下仅为我的应用程序区域更改NSCursor形状?

推荐答案

如果我们查阅the NSCursor documentation,我们会找到关于"光标矩形"的讨论:

在Cocoa中,您可以根据鼠标在某个视图上的位置来更改当前显示的光标.您可以使用此技术来提供有关用户可以使用鼠标执行哪些操作的可视反馈.例如,每当鼠标移动到用作自定义大小调整手柄的视图部分上时,您可能会显示其中一个调整大小的光标.要进行此设置,您需要将一个光标对象与视图中的一个或多个光标矩形相关联.

让我们写一个ViewModifier来设置一个光标矩形.

extension View {
  func cursor(_ cursor: NSCursor) -> some View {
    self.modifier(CursorModifier(cursor: cursor))
  }
}

struct CursorModifier: ViewModifier {
  let cursor: NSCursor

  func body(content: Content) -> some View {
    content
      .background { Rep(cursor: cursor) }
  }
}

修改器会将类型为Repbackground视图添加到修改后的视图中.但是Rep是什么呢?它是一款NSViewRepresentable,包装了一款定制的NSView.NSView覆盖resetCursorRects以创建覆盖整个NSView的光标矩形.

extension CursorModifier {
  private struct Rep: NSViewRepresentable {
    let cursor: NSCursor

    func makeNSView(context: Context) -> NSViewType {
      return NSViewType(cursor: cursor)
    }

    func updateNSView(_ nsView: NSViewType, context: Context) {
      nsView.cursor = cursor
    }

    class NSViewType: NSView {
      var cursor: NSCursor

      init(cursor: NSCursor) {
        self.cursor = cursor
        super.init(frame: .zero)
      }
      
      required init?(coder: NSCoder) { fatalError() }

      override func resetCursorRects() {
        addCursorRect(bounds, cursor: cursor)
      }
    }
  }
}

为了测试它,我将设置一个SwiftUI视图,其中包含Text(不应影响光标)和TextField(悬停时应显示the iBeam cursor).

struct ContentView: View {
  @State var text: String = ""

  var body: some View {

    VStack {
      Spacer()
      Text("Hello, world!")
      TextField("Input:", text: $text)
      Spacer()
    }
    .padding()
    .cursor(.pointingHand)
  }
}

结果:

Demo of the mouse pointer moving up and down the window. The cursor changes from the standard arrow to the pointing hand when it is in the window but not in the text field. It changes to the i-beam cursor when it is over the text field.

请注意,我必须将Spacer放在VStack中,才能让VStack填满窗口.否则,窗口可以比VStack更高,并且VStack之外的窗口部分不使用指点手.

Swift相关问答推荐

在swift静态var扩展中,如何或可以访问和返回具体类?

依赖于@Environment的init@StateObject

try 在小部件内部读取核心数据的SwiftUI总是返回空吗?

NSApplication是否需要NSApplicationDelegate?

插入/删除视图时的Swiftui动态过渡

如何避免切换视图递归onChange调用SwiftUI

使用序列初始化字符串的时间复杂度是多少?

URL appendPathComponent(_:) 已弃用?

使用 RxSwift 围绕 async/await 方法创建 Observable

SwiftUI 动画的范围

SwiftUI:决定键盘重叠的内容

如何在 Swift 中的两个 UIView 中心之间添加线?

'NSLog' 不可用:可变参数函数在 swift 中不可用

在 Swift for iOS 中沿圆形路径绘制文本

3D touch /强制touch 实现

如何使用 swift 在 tableview 中填充两个不同数组的两个部分?

如何在 SwiftUI 中的一个视图上显示两个alert ?

如何将 Int 拆分为其各个数字?

Swift - 要求实现协议的类是某个类的子类

如何在 iOS 上的 Swift 中获取 sharedApplication?