TL; DR :我使用Path
指定Image
的匹配区域。但是我不知道如何调整Path
坐标以匹配SwiftUI决定的布局...或者甚至不需要这样做。
运行时
我的(测试)应用看起来像这样,为清晰起见,Image
边框(不是框架)已上色:
我想要是要用该Image
处理不透明橙色中的水龙头。即使在橙色图像的边界内,不透明橙色以外的拍子也应“掉入”绿色图像或灰色背景。但不是。 (紫色轮廓根据其自身显示了路径的位置;请参见下面的代码)。
详细信息
这是图像的固有(以像素为单位)布局:
不透明部分周围的路径被视为
[(200, 200), (600, 200), (600, 600), (200, 600)]
这些坐标和Image
坐标如何关联?
代码
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.
}
}
}
答案 0 :(得分:0)
1).offset
仅移动内容,但视图在视图层次结构布局中的位置未更改,因此请改用.position
;
2)contentShape
的形状应在视图坐标空间中构建
答案 1 :(得分:0)
阿斯佩里(Asperi)是正确的,当我读Paul Hudson并抓住了Shape
的(单个)要求时,我意识到了这一点—一条路径( in rect:CGRect )- >路径方法。 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.