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

问题描述 投票:0回答:1

我正在开发一个 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") 
      } 
   } 
swiftui transparency alpha swiftui-ontapgesture
1个回答
0
投票

这里的关键是将图像的

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)
    }
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.