我正在开发一个 iOS 应用程序(使用 SwiftUI),其中用户必须点击图像才能获得响应。该图像覆盖了另一个背景图像,因此有意具有一定的透明度。在某些图像中存在大量透明空间。这使得“空白空间”中的可点击区域过多。我想改进它,以便只有可见部分响应点击手势。
截至目前,用户点击手势的任何地方都会做出响应……即使用户点击图像的透明部分也是如此。我在这里发现了一个类似的问题Make onlyvisible parts of png clickable UIbutton swift,但它似乎处理ZStack中的多个图像。这是我的一些代码:
Image("dog")
.resizable()
.scaledToFit()
.zIndex(1)
.clipped()
.gesture(simpleTap)
var simpleTap: some Gesture {
TapGesture()
.onEnded { _ in
playSound("dog.mp3")
}
}
这里的关键是将图像的
contentShape
设置为代表图像轮廓的Shape
。
如果您的图像是静态图像,那么您只需使用图像编辑器找到轮廓并将其导出为矢量图形格式。例如,在 GIMP 中,您可以选择图像的非透明部分,将选择转换为路径,然后将该路径导出到 SVG。 (参见 将任何图像文件的不透明部分转换为单个 svg 路径的最简单方法是什么?)然后您可以使用 PocketSVG 之类的东西读取 SVG,获取
CGPath
,然后创建 SwiftUI Path
来自它。
let svgURL = Bundle.main.url(forResource: "foo", withExtension: "svg")!
let paths = SVGBezierPath.pathsFromSVG(at: svgURL)
// you can save the 2 things above somewhere else, so that it doesn't get
// recomputed every time the view updates.
Image("dog")
.contentShape(Path(paths.first!.cgPath))
.onTapGesture {
print("Tapped")
}
.overlay { // overlay showing you where the tappable area is
Path(paths.first!.cgPath).stroke(lineWidth: 1)
}
如果图像可调整大小,则应相应调整路径大小。您可以创建自己的
Shape
来执行此操作:
struct ResizablePath: Shape {
let path: Path
let originalSize: CGSize
func path(in rect: CGRect) -> Path {
let xScale = rect.width / originalSize.width
let yScale = rect.height / originalSize.height
print(path.boundingRect)
return path.applying(.init(scaleX: xScale, y: yScale).translatedBy(x: rect.minX, y: rect.minY))
}
}
let svgURL = Bundle.main.url(forResource: "foo", withExtension: "svg")!
let paths = SVGBezierPath.pathsFromSVG(at: svgURL)
// get the original size from a UIImage
let size = UIImage(named: "dog")!.size
Image("dog")
.resizable()
.contentShape(ResizablePath(path: Path(paths.first!.cgPath), originalSize: size))
.scaledToFit()
.onTapGesture {
print("Tapped")
}
.overlay {
ResizablePath(path: Path(paths.first!.cgPath), originalSize: size).stroke(lineWidth: 1)
}