SwiftUI- 仅使 png 图像的可见部分响应点击手势

SwiftUI- Make Only the Visible Part of png Image Respond to Tap Gesture

提问人:hoptown 提问时间:11/7/2023 最后编辑:hoptown 更新时间:11/8/2023 访问量:41

问:

我正在开发一个 iOS 应用程序(使用 SwiftUI),用户必须在其中点击图像才能获得响应。图像覆盖了另一个背景图像,因此它有意具有一些透明度。在一些图像中,有很多透明空间。这允许在“空白空间”中占用太多的可点击区域。我想对其进行改进,以便只有可见部分对点击手势做出响应。

截至目前,无论用户点击手势的哪个位置都会响应......即使用户点击图像的透明部分。我在这里发现了一个类似的问题 Make only visible 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") 
      } 
   } 
SwiftUI 透明度 alpha swiftui-ontapgesture

评论

0赞 ScottM 11/8/2023
我不相信有一种机制可以使用 PNG 本身的 alpha 透明度。如果可以,则可以使用指定命中区域的修饰符向图像添加修饰符。问题在于,只有当您知道相关图形并能够生成与逐像素光栅化 PNG 匹配的贝塞尔曲线区域时,它才真正有效。contentShape()Shape

答:

0赞 Sweeper 11/8/2023 #1

这里的关键是将图像的 设置为表示图像轮廓的 a。contentShapeShape

如果您的图像是静态图像,那么您只需使用图像编辑器即可查找轮廓并将其导出为矢量图形格式。例如,在 GIMP 中,您可以选择图像的非透明部分,将所选内容转换为路径,然后将该路径导出为 SVG。(请参阅将任何图像文件的非透明部分转换为单个 svg 路径的最简单方法是什么?然后,您可以使用类似 PocketSVG 的东西读取 SVG,获取 ,并从中创建 SwiftUI。CGPathPath

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