Layout 快捷方式路径作为contentShape,不';与图像不一致

Layout 快捷方式路径作为contentShape,不';与图像不一致,layout,swiftui,mask,hittest,Layout,Swiftui,Mask,Hittest,TL;DR:我正在使用路径为图像指定命中区域。但我不知道如何调整路径坐标以匹配SwiftUI决定的布局。。。或者,如果我需要的话 运行时 #!/usr/bin/python3 # From https://www.reddit.com/r/Python/comments/f2kv1/question_on_tracing_an_image_in_python_with_pil import sys import os os.environ['PYGAME_HIDE_SUPPORT_PROMP

TL;DR:我正在使用
路径
图像指定命中区域。但我不知道如何调整路径坐标以匹配SwiftUI决定的布局。。。或者,如果我需要的话

运行时

#!/usr/bin/python3

# From https://www.reddit.com/r/Python/comments/f2kv1/question_on_tracing_an_image_in_python_with_pil

import sys
import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame

# Find simple border.

fname   = sys.argv[1]
image   = pygame.image.load(fname)
bitmask = pygame.mask.from_surface(image) 
comp    = bitmask.connected_component() 
outline = comp.outline(48)

print("name:  ", fname)
print("size:  ", image.get_rect().size)
print("points:", outline)

# Sanity check.
# From https://www.geeksforgeeks.org/python-pil-imagedraw-draw-polygon-method

from PIL import Image, ImageDraw, ImagePath
import math

box = ImagePath.Path(outline).getbbox()
bsize = list(map(int, map(math.ceil, box[2:])))

im = Image.new("RGB", bsize, "white")
draw = ImageDraw.Draw(im)
draw.polygon(outline, fill="#e0c0ff", outline="purple")

im.show()      # That this works is amazing.
我的(测试)应用程序如下所示,
图像
边框(非边框)为清晰起见上色:

我想要的是不透明橙色中的点击由该
图像处理。在不透明橙色外的点击-即使在橙色图像的边界内-应“穿过”绿色图像或灰色背景。但是没有。(紫色的轮廓显示了路径所在的位置,根据路径本身;请参见下面的代码)

详细信息

extension CGPoint {
    func scaled(xFactor:CGFloat, yFactor:CGFloat) -> CGPoint {
        return CGPoint(x: x * xFactor, y: y * yFactor)
    }

    typealias SelfMap = (CGPoint) -> CGPoint
    static func scale(_ designSize: CGSize, into displaySize: CGSize) -> SelfMap {{
        $0.scaled(
            xFactor: displaySize.width  / designSize.width,
            yFactor: displaySize.height / designSize.height
        )
    }}

    typealias Tuple = (x:CGFloat, y:CGFloat)
    init(tuple t: Tuple) {
        self.init(x: t.x, y: t.y)
    }
}
以下是图像的固有(像素)布局:

不透明部分周围的路径通常被视为

[(200, 200), (600, 200), (600, 600), (200, 600)] 
这些坐标与
图像
坐标有何关系

代码

extension CGPoint {
    typealias Tuple = (x:CGFloat, y:CGFloat)
    init(tuple t: Tuple) {
        self.init(x: t.x, y: t.y)
    }
}

struct ContentView: View {

    var points = [(200, 200), (600, 200), (600, 600), (200, 600)]
        .map(CGPoint.init(tuple:))
        // .map { p in p.shifted(dx:-64, dy:-10)}  // Didn't seem to help.

    var path : Path {
        var result = Path()
        result.move(to: points.first!)
        result.addLines(points)
        result.closeSubpath()
        return result
    }

    var body: some View {
        ZStack{
            Image("gray")       // Just so we record all touches.
            .resizable()
            .frame(maxWidth : .infinity,maxHeight: .infinity)
            .onTapGesture {
                print("background")
            }

            Image("square_green")
            .resizable()
            .scaledToFit()
            .border(Color.green, width: 4)
            .offset(x: 64, y:10)    // So the two Images don't overlap completely.
            .onTapGesture {
                print("green")
            }

            Image("square_orange")
            .resizable()
            .scaledToFit()
            .contentShape(path)        // Magic should happen here.
            .border(Color.orange, width: 4)
            .offset(x: -64, y:-10)
            // .contentShape(path)     // Didn't work here either.
            .onTapGesture {
                 print("orange")
            }

            path.stroke(Color.purple)  // Origin at absolute (200,200) as expected.

        }
    }
}
1)
.offset
仅移动内容,但视图在视图层次结构布局中的位置不会更改,因此使用
.position


2)
contentShape
的形状应该在视图坐标空间中构造

Asperi是正确的,当我阅读并掌握了
shape
-a path(in rect:CGRect)->path方法的(单一)要求时,我才明白这一点。
rect
参数告诉您需要了解的关于局部坐标系的所有信息:即其大小

我的工作代码现在看起来像这样

助手

extension CGPoint {
    func scaled(xFactor:CGFloat, yFactor:CGFloat) -> CGPoint {
        return CGPoint(x: x * xFactor, y: y * yFactor)
    }

    typealias SelfMap = (CGPoint) -> CGPoint
    static func scale(_ designSize: CGSize, into displaySize: CGSize) -> SelfMap {{
        $0.scaled(
            xFactor: displaySize.width  / designSize.width,
            yFactor: displaySize.height / designSize.height
        )
    }}

    typealias Tuple = (x:CGFloat, y:CGFloat)
    init(tuple t: Tuple) {
        self.init(x: t.x, y: t.y)
    }
}
在适当的上下文中绘制路径

// This is just the ad-hoc solution. 
// You will want to parameterize the designSize and points.

let designSize = CGSize(width:800, height:800)
let opaqueBorder = [(200, 200), (600, 200), (600, 600), (200, 600)]

// To find boundary of real-life images, see Python code below.

struct Mask : Shape {
    func path(in rect: CGRect) -> Path {
        let points = opaqueBorder
            .map(CGPoint.init(tuple:))

            // *** Here we use the context *** (rect.size)
            .map(CGPoint.scale(designSize, into:rect.size))

        var result = Path()
        result.move(to: points.first!)
        result.addLines(points)
        result.closeSubpath()
        return result
    }
}
使用遮罩

struct ContentView: View {
    var body: some View {
        ZStack{
            Image("gray")           // Just so we record all touches.
            .resizable()
            .frame(
                maxWidth : .infinity,
                maxHeight: .infinity
            )
            .onTapGesture {
                    print("background")
            }

            // Adding mask here left as exercise.
            Image("square_green")
            .resizable()
            .scaledToFit()
            .border(Color.green, width: 4)
            .offset(x: 64, y:10)    // So the two Images don't overlap completely.
            .onTapGesture {
                print("green")
            }

            Image("square_orange")
            .resizable()
            .scaledToFit()
            .border(Color.orange, width: 4)

           // Sanity check shows the Mask outline.
            .overlay(Mask().stroke(Color.purple))

            // *** Actual working Mask ***
            .contentShape(Mask())

            .offset(x: -64, y:-10)
            .onTapGesture {
                print("orange")
            } 
        }
    }
}
获取大纲

#!/usr/bin/python3

# From https://www.reddit.com/r/Python/comments/f2kv1/question_on_tracing_an_image_in_python_with_pil

import sys
import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame

# Find simple border.

fname   = sys.argv[1]
image   = pygame.image.load(fname)
bitmask = pygame.mask.from_surface(image) 
comp    = bitmask.connected_component() 
outline = comp.outline(48)

print("name:  ", fname)
print("size:  ", image.get_rect().size)
print("points:", outline)

# Sanity check.
# From https://www.geeksforgeeks.org/python-pil-imagedraw-draw-polygon-method

from PIL import Image, ImageDraw, ImagePath
import math

box = ImagePath.Path(outline).getbbox()
bsize = list(map(int, map(math.ceil, box[2:])))

im = Image.new("RGB", bsize, "white")
draw = ImageDraw.Draw(im)
draw.polygon(outline, fill="#e0c0ff", outline="purple")

im.show()      # That this works is amazing.

是的,是的。特别是第2点)。我在发布的解决方案中保留了
偏移量。我不得不给我的继任者留下一些工作。