You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 06:25:54