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

SwiftUI自定义形状:顶部圆角+底部撕纸锯齿效果实现指引

SwiftUI 实现顶部圆角+底部撕纸锯齿效果的自定义Shape

我正在用SwiftUI开发,需要实现一个自定义Shape:整体是矩形,顶部带圆角,底部边缘是一排带圆角的三角形,模拟撕纸效果。目前尝试的代码如下,但效果不符合预期:

struct CustomShape: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()

        let width = rect.size.width
        let height = rect.size.height

        let bottomLeft = CGPoint(x: 0, y: height)
        let bottomRight = CGPoint(x: width, y: height)
        let topLeft = CGPoint(x: 0, y: 0)
        let topRight = CGPoint(x: width, y: 0)

        path.move(to: bottomRight)

        path.addLine(to: CGPoint(x: bottomRight.x, y: topRight.y))

        path.addLine(to: CGPoint(x: topLeft.x, y: topLeft.y))
        
        path.addLine(to: CGPoint(x: bottomLeft.x, y: bottomLeft.y))
        
        let triangleWidth: CGFloat = 11
        let triangleHeight: CGFloat = 6

        var x: CGFloat = 0
        while x <= rect.width {
            let startX = x
            let endX = x + triangleWidth
            let midX = (startX + endX) / 2

            path.move(to: CGPoint(x: startX, y: rect.maxY))
            path.addLine(to: CGPoint(x: midX, y: rect.maxY - triangleHeight))
            path.addLine(to: CGPoint(x: endX, y: rect.maxY))

            x += triangleWidth
        }

        return path
    }
}

现有代码问题分析

你的代码存在两个核心问题:

  • 先绘制了完整的矩形底部边缘,之后又叠加绘制三角形,导致路径重叠混乱,Shape的填充/描边会出现错误。
  • 没有实现顶部的圆角效果,且底部的三角形是尖角,不符合“圆角三角形”的撕纸需求。

优化实现方案

正确的做法是构建连续的路径,从顶部圆角开始,沿着左侧边缘到左下角,然后逐个绘制带圆角的底部锯齿,最后回到右上角闭合路径。以下是优化后的实现:

struct TornPaperShape: Shape {
    // 可配置参数,方便根据设计调整
    var topCornerRadius: CGFloat = 12
    var toothWidth: CGFloat = 11
    var toothHeight: CGFloat = 6
    var toothCornerRadius: CGFloat = 2
    
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let width = rect.width
        let height = rect.height
        
        // 1. 绘制顶部圆角:从右上角到左上角
        path.move(to: CGPoint(x: width, y: topCornerRadius))
        path.addArc(center: CGPoint(x: width - topCornerRadius, y: topCornerRadius),
                    radius: topCornerRadius,
                    startAngle: .zero,
                    endAngle: .degrees(90),
                    clockwise: false)
        path.addLine(to: CGPoint(x: topCornerRadius, y: 0))
        path.addArc(center: CGPoint(x: topCornerRadius, y: topCornerRadius),
                    radius: topCornerRadius,
                    startAngle: .degrees(90),
                    endAngle: .degrees(180),
                    clockwise: false)
        path.addLine(to: CGPoint(x: 0, y: height - toothHeight))
        
        // 2. 绘制底部圆角锯齿
        var currentX: CGFloat = 0
        while currentX < width {
            let nextX = min(currentX + toothWidth, width)
            let midX = currentX + (nextX - currentX) / 2
            
            // 从当前点到锯齿底部(带圆角过渡)
            path.addArc(center: CGPoint(x: currentX, y: height),
                        radius: toothCornerRadius,
                        startAngle: .degrees(180),
                        endAngle: .degrees(270),
                        clockwise: false)
            path.addLine(to: CGPoint(x: midX, y: height - toothHeight))
            path.addArc(center: CGPoint(x: nextX, y: height),
                        radius: toothCornerRadius,
                        startAngle: .degrees(90),
                        endAngle: .degrees(0),
                        clockwise: false)
            
            currentX = nextX
        }
        
        // 3. 闭合路径,回到起点
        path.addLine(to: CGPoint(x: width, y: height))
        path.addLine(to: CGPoint(x: width, y: topCornerRadius))
        
        return path
    }
}

关键细节说明

  • 所有参数都做成可配置的,你可以根据设计需求调整圆角大小、锯齿尺寸。
  • 路径全程连续绘制,没有多余的move(to:)操作,避免路径重叠导致的渲染错误。
  • 底部锯齿的圆角通过addArc实现,模拟真实撕纸的圆润边缘。
  • 顶部圆角用标准圆弧绘制,符合常规圆角矩形的视觉效果。

使用示例

你可以像普通Shape一样直接使用:

struct ContentView: View {
    var body: some View {
        TornPaperShape()
            .fill(Color.white)
            .frame(width: 300, height: 200)
            .shadow(color: .gray.opacity(0.3), radius: 4, x: 0, y: 2)
    }
}

内容的提问来源于stack exchange,提问作者Danila Belyi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 00:31:24