SwiftUI中如何将任意路径在不同坐标系间转换(自定义Y轴方向)
SwiftUI中如何将任意路径在不同坐标系间转换(自定义Y轴方向)
你现在有一段SwiftUI代码,通过不同方式绘制了几个Path,想要把坐标系从默认的Y轴向下(黑色坐标系)改成Y轴向上的形式(蓝色坐标系),而且只能基于已有的Path对象进行转换,不能依赖创建Path时的原始CGRect,对吧?
其实实现这个需求的核心是对Path应用坐标变换,SwiftUI提供了非常方便的transformEffect修饰符来做这件事,而且是GPU加速的,比手动修改每个Path点高效得多。
核心思路
默认SwiftUI坐标系的原点在左上角,Y轴向下;我们要改成原点在左下角,Y轴向上。这个转换可以通过两步仿射变换实现:
- 先对Y轴进行翻转(缩放Y为-1),这会让Path上下颠倒
- 再将整个Path向下偏移容器的高度,把翻转后的Path对齐到新坐标系的底部
因为我们需要容器的尺寸来计算偏移量,所以必须在GeometryReader中完成这个变换。
修改后的完整代码
下面是对你的原始代码的修改版本,已经加入了坐标系转换的逻辑:
import SwiftUI struct ContentView: View { var body: some View { GeometryReader { proxy in let containerSize = proxy.size // 定义坐标系转换的仿射变换 let coordinateTransform = CGAffineTransform(scaleX: 1, y: -1) .translatedBy(x: 0, y: -containerSize.height) Color.yellow // way 1 with offsetBy: rectPath1 .offsetBy(dx: .zero, dy: .zero) .fill(Color.blue) .transformEffect(coordinateTransform) // 应用坐标系转换 // way 2 with position: rectPath2 .fill(Color.red) .position(x: 0, y: 0) .transformEffect(coordinateTransform) // way 3 with offset: rectPath3 .fill(Color.green) .offset(x: 0, y: 0) .transformEffect(coordinateTransform) // 如果你想给所有内容统一应用变换,可以把整个GeometryReader的内容包起来 // 比如: // ZStack { // // 所有Path和背景色 // } // .transformEffect(coordinateTransform) } } } func createRectPath(for rect: CGRect) -> Path { var path = Path() path.addRect(rect) return path } let rectPath1: Path = createRectPath(for: CGRect(x: 50, y: 50, width: 50, height: 100)) let rectPath2: Path = createRectPath(for: CGRect(x: 200, y: 100, width: 100, height: 100)) let rectPath3: Path = createRectPath(for: CGRect(x: 400, y: 300, width: 100, height: 100))
额外说明
- 统一变换 vs 单个变换:如果所有Path都需要转换坐标系,你可以把所有内容放在一个
ZStack里,然后给ZStack加transformEffect,这样就不用给每个Path单独加了,更简洁。 - 手动修改Path点的方式(备选):如果你真的需要手动遍历Path的每个点进行转换(比如有特殊需求),可以用
Path的forEach { element in ... }方法遍历路径元素,然后重新构建Path。不过这种方式效率不如transformEffect,一般不推荐:
func convertPathToUpsideDown(_ path: Path, containerHeight: CGFloat) -> Path { var convertedPath = Path() path.forEach { element in switch element { case .move(to: let point): convertedPath.move(to: CGPoint(x: point.x, y: containerHeight - point.y)) case .line(to: let point): convertedPath.addLine(to: CGPoint(x: point.x, y: containerHeight - point.y)) case .quadCurve(to: let to, control: let control): convertedPath.addQuadCurve(to: CGPoint(x: to.x, y: containerHeight - to.y), control: CGPoint(x: control.x, y: containerHeight - control.y)) case .curve(to: let to, control1: let c1, control2: let c2): convertedPath.addCurve(to: CGPoint(x: to.x, y: containerHeight - to.y), control1: CGPoint(x: c1.x, y: containerHeight - c1.y), control2: CGPoint(x: c2.x, y: containerHeight - c2.y)) case .closeSubpath: convertedPath.closeSubpath() } } return convertedPath }
你可以在GeometryReader中调用这个函数:
convertPathToUpsideDown(rectPath1, containerHeight: containerSize.height) .fill(Color.blue)
备注:内容来源于stack exchange,提问作者swiftPunk
相关产品推荐
相关产品推荐

