SwiftUI中使用自定义Path解决尺寸与形状变形问题
自定义六边形组合形状变形问题解决
我实现了一个HorizontalHexagon可嵌入形状,初始效果正常:
struct HorizontalHexagon: InsettableShape { var insetAmount: CGFloat = CGFloat.zero func path(in rect: CGRect) -> Path { return Path { path in path.move(to: CGPoint(x: rect.minX + insetAmount, y: rect.midY)) path.addLine(to: CGPoint(x: rect.maxX/4.0, y: rect.minY + insetAmount)) path.addLine(to: CGPoint(x: rect.maxX - rect.maxX/4.0, y: rect.minY + insetAmount)) path.addLine(to: CGPoint(x: rect.maxX - insetAmount, y: rect.midY)) path.addLine(to: CGPoint(x: rect.maxX - rect.maxX/4.0, y: rect.maxY - insetAmount)) path.addLine(to: CGPoint(x: rect.maxX/4.0, y: rect.maxY - insetAmount)) path.closeSubpath() } } func inset(by amount: CGFloat) -> some InsettableShape { var myShape: Self = self myShape.insetAmount += amount return myShape } }

但用它构建FinalShape时出现尺寸和形状变形:
struct FinalShape: InsettableShape { var insetAmount: CGFloat = CGFloat.zero let size1: Double = 4.0 func path(in rect: CGRect) -> Path { return Path { path in let makeSize1: Double = rect.width/size1 let makeSize2: Double = rect.width - makeSize1 let horizontalHexagon = HorizontalHexagon(insetAmount: insetAmount) path.addPath(horizontalHexagon.path(in: CGRect(origin: CGPoint(x: rect.minX + makeSize1/2, y: rect.minY), size: CGSize(width: makeSize2, height: makeSize1)))) path.addPath(horizontalHexagon.path(in: CGRect(origin: CGPoint(x: rect.minX + makeSize1/2, y: rect.midY - makeSize1/2), size: CGSize(width: makeSize2, height: makeSize1)))) path.addPath(horizontalHexagon.path(in: CGRect(origin: CGPoint(x: rect.minX + makeSize1/2, y: rect.maxY - makeSize1), size: CGSize(width: makeSize2, height: makeSize1)))) } } func inset(by amount: CGFloat) -> some InsettableShape { var myShape: Self = self myShape.insetAmount += amount return myShape } }

ContentView代码:
struct ContentView: View { var body: some View { FinalShape() .strokeBorder(style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round)) .background(Color.purple) .cornerRadius(5.0) .padding() } }
我更新了HorizontalHexagon代码,但问题依然存在:
struct HorizontalHexagon: InsettableShape { var insetAmount: CGFloat = CGFloat.zero func path(in rect: CGRect) -> Path { return Path { path in path.move(to: CGPoint(x: rect.minX + insetAmount, y: rect.midY)) path.addLine(to: CGPoint(x: rect.width/4, y: rect.minY + insetAmount)) path.addLine(to: CGPoint(x: rect.maxX - rect.width/4, y: rect.minY + insetAmount)) path.addLine(to: CGPoint(x: rect.maxX - insetAmount, y: rect.midY)) path.addLine(to: CGPoint(x: rect.maxX - rect.width/4, y: rect.maxY - insetAmount)) path.addLine(to: CGPoint(x: rect.width/4, y: rect.maxY - insetAmount)) path.closeSubpath() } } func inset(by amount: CGFloat) -> some InsettableShape { var myShape: Self = self myShape.insetAmount += amount return myShape } }
问题原因及解决方案
1. 核心问题:路径坐标与宽高比例错误
- 更新后的
HorizontalHexagon仍存在坐标计算错误:比如rect.width/4未加上rect.minX,导致六边形放在非原点矩形内时,左侧点位置偏移。 FinalShape中给六边形分配的矩形宽高比例严重失衡,直接拉伸了六边形形状。
2. 修正后的HorizontalHexagon代码
确保所有坐标基于当前矩形原点,同时适配合理的宽高比例:
struct HorizontalHexagon: InsettableShape { var insetAmount: CGFloat = 0 func path(in rect: CGRect) -> Path { let insetRect = rect.insetBy(dx: insetAmount, dy: insetAmount) let halfHeight = insetRect.height / 2 let leftOffset = insetRect.width / 4 return Path { path in // 左侧中点 path.move(to: CGPoint(x: insetRect.minX, y: insetRect.midY)) // 左上点 path.addLine(to: CGPoint(x: insetRect.minX + leftOffset, y: insetRect.minY)) // 右上点 path.addLine(to: CGPoint(x: insetRect.maxX - leftOffset, y: insetRect.minY)) // 右侧中点 path.addLine(to: CGPoint(x: insetRect.maxX, y: insetRect.midY)) // 右下点 path.addLine(to: CGPoint(x: insetRect.maxX - leftOffset, y: insetRect.maxY)) // 左下点 path.addLine(to: CGPoint(x: insetRect.minX + leftOffset, y: insetRect.maxY)) // 闭合路径 path.closeSubpath() } } func inset(by amount: CGFloat) -> some InsettableShape { var shape = self shape.insetAmount += amount return shape } }
3. 修正FinalShape的布局逻辑
给每个六边形分配匹配的宽高比例,保证形状不变形,同时均匀布局:
struct FinalShape: InsettableShape { var insetAmount: CGFloat = 0 let hexagonCount = 3 let horizontalPaddingRatio = 1.0 / 4.0 func path(in rect: CGRect) -> Path { let horizontalPadding = rect.width * horizontalPaddingRatio let hexagonWidth = rect.width - horizontalPadding * 2 // 用宽度的一半作为高度,保证六边形形状比例正确 let hexagonHeight = hexagonWidth / 2 let verticalSpacing = (rect.height - hexagonHeight * CGFloat(hexagonCount)) / CGFloat(hexagonCount + 1) let hexagon = HorizontalHexagon(insetAmount: insetAmount) return Path { path in // 第一个六边形 let firstY = verticalSpacing path.addPath(hexagon.path(in: CGRect( x: rect.minX + horizontalPadding, y: firstY, width: hexagonWidth, height: hexagonHeight ))) // 第二个六边形 let secondY = firstY + hexagonHeight + verticalSpacing path.addPath(hexagon.path(in: CGRect( x: rect.minX + horizontalPadding, y: secondY, width: hexagonWidth, height: hexagonHeight ))) // 第三个六边形 let thirdY = secondY + hexagonHeight + verticalSpacing path.addPath(hexagon.path(in: CGRect( x: rect.minX + horizontalPadding, y: thirdY, width: hexagonWidth, height: hexagonHeight ))) } } func inset(by amount: CGFloat) -> some InsettableShape { var shape = self shape.insetAmount += amount return shape } }
修改后,三个六边形会保持正确的形状,且在父容器内均匀分布,不会出现变形和错位问题。
内容的提问来源于stack exchange,提问作者swiftPunk
相关产品推荐
相关产品推荐

