SwiftUI自定义iPadOS上的鼠标指针

3

如何在 SwiftUI + iPadOS 上使用自定义鼠标指针?

苹果提供了一个示例代码,但是该代码适用于 UIKit: https://developer.apple.com/documentation/uikit/pointer_interactions/enhancing_your_ipad_app_with_pointer_interactions

class QuiltView: UIView, UIPointerInteractionDelegate {


...
    


func sharedInit() {
    ...

    // Add a pointer interaction for the "editor" area Quilt view.
    self.addInteraction(UIPointerInteraction(delegate: self))
}

// Supply custom regions that correspond to grid lines when "useStraightLineStitch" is enabled.
func pointerInteraction(_ interaction: UIPointerInteraction,
                        regionFor request: UIPointerRegionRequest,
                        defaultRegion: UIPointerRegion) -> UIPointerRegion? {
    if useStraightLineStitch {
        let regionNumber = floor(request.location.y / gridHeight)
        return UIPointerRegion(rect: CGRect(x: 0, y: regionNumber * gridHeight, width: self.bounds.size.width, height: gridHeight))
    } else {
        return defaultRegion
    }
}

// Supply pointer style with custom crosshair shape.
func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? {
    let crosshair = UIPointerShape.path(crosshairBezierPath())

    if useStraightLineStitch {
        return UIPointerStyle(shape: crosshair, constrainedAxes: [.vertical])
    } else {
        return UIPointerStyle(shape: crosshair)
    }
}

func crosshairBezierPath() -> UIBezierPath {
    let crosshairPath = UIBezierPath()
    crosshairPath.move(to: CGPoint(x: -1.5, y: 1.5))
    crosshairPath.addLine(to: CGPoint(x: -1.5, y: 10))
    crosshairPath.addArc(withCenter: CGPoint(x: 0, y: 10),
                         radius: 1.5,
                         startAngle: CGFloat.pi,
                         endAngle: 0,
                         clockwise: false)
    crosshairPath.addLine(to: CGPoint(x: 1.5, y: 1.5))
    crosshairPath.addLine(to: CGPoint(x: 10, y: 1.5))
    crosshairPath.addArc(withCenter: CGPoint(x: 10, y: 0),
                         radius: 1.5,
                         startAngle: CGFloat.pi / 2.0,
                         endAngle: CGFloat.pi * 1.5,
                         clockwise: false)
    crosshairPath.addLine(to: CGPoint(x: 1.5, y: -1.5))
    crosshairPath.addLine(to: CGPoint(x: 1.5, y: -10))
    crosshairPath.addArc(withCenter: CGPoint(x: 0, y: -10),
                         radius: 1.5,
                         startAngle: 0,
                         endAngle: CGFloat.pi,
                         clockwise: false)
    crosshairPath.addLine(to: CGPoint(x: -1.5, y: -1.5))
    crosshairPath.addLine(to: CGPoint(x: -10, y: -1.5))
    crosshairPath.addArc(withCenter: CGPoint(x: -10, y: 0),
                         radius: 1.5,
                         startAngle: CGFloat.pi * 1.5,
                         endAngle: CGFloat.pi / 2.0,
                         clockwise: false)
    crosshairPath.close()
    return crosshairPath
}


...

我希望使用SwiftUI实现相同的功能,您能帮忙翻译吗?

提前感谢。

1个回答

2
如果你想使用苹果当前的API来完成这个功能,我不认为这是你可以做到的。希望在未来的版本中会有这样的功能。
但是,你仍然可以通过一些内省来启用此功能。
首先,一个警告:虽然常见的UIKit元素支持iOS上的SwiftUI视图,但这并不能保证在所有情况下都是正确的。因此,这种解决方案可能在将来某个时候失效。
现在,介绍一种可能的方法。
向你的应用程序添加一个内省包。过去,我曾经使用SwiftUI-Introspect(将UIDropInteraction添加到我的视图中)。然后将Introspect模块导入到你的源文件中。
大多数内省包,如此类,包括View扩展,让你检查底层的UIKit组件。根据你的视图,你将使用特定的修饰符。
例如,如果您想要获取特定视图的基础视图控制器,您将使用 .introspectViewController() 修改器。您提供一个闭包,该闭包将被赋予视图控制器。您可以将交互添加到视图控制器的视图中。(如果您有一个滚动视图,请使用 introspectScrollView 修改器等)
例如,假设有一个名为 PointerDelegate 的类符合 UIPointerInteractionDelegate
import SwiftUI
import Introspect

struct YourView: View {

  var body: some View {
    // Your view hierarchy
    // For instance
    List { ... }
      .introspectViewController { viewController in 
        viewController.view.addInteraction(UIPointerInteraction(delegate: PointerDelegate()))
      }
  }
}

这是一种有点取巧的方法,正如之前所警告的那样,它可能在将来失效。然而,这是一种在你的SwiftUI应用程序中访问一些仅在UIKit中启用的功能的方法。

嘿,Josh,谢谢你的建议,你的建议真的很有趣,但是对我来说在使用UIPointerInteraction时没有起作用。 也许一个可行的方法是使用UIViewRepresentable功能,但是我还需要学习一些关于这个的知识。 - Tiago Mendes
@TiagoMendes 今天早上试了一下,有几点评论:(1) 你需要持久化你的代理。将其设为单例并将其传递给UIPointerInteraction(delegate:)调用应该可以工作。(2) 除非你将其添加到UIKit中的特定对象,比如UITextField或UITableView,否则很难进行内省。如果你只是寻找一个UIView,很容易得到错误的视图,或者一个没有宽度/高度的视图。 - Josh Hrach
1
我将您的答案标记为正确,您的解决方案基本上有效。每当我点击时,光标会消失,然后圆圈会回来,并且在某些元素中,只显示圆圈。但是有这个总比没有好。非常感谢您的回复和时间,希望SwiftUI团队能够支持这个:D - Tiago Mendes
不客气!很高兴能帮到你。我也希望SwiftUI能原生支持更多这样的功能。 - Josh Hrach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接