SwiftUI 自定义 Shape:实现顶部圆角矩形 RoundedTopRectangle

在 SwiftUI 里,如果你只想要一个"顶部两个角是圆角,底部是直角"的矩形,第一反应可能是用 .cornerRadius()

.cornerRadius() 会同时作用在四个角,没办法单独控制某些角,这时候就需要自定义 Shape

这篇文章,我们就从零实现一个 RoundedTopRectangle,并详细解释绘制的每一步。

为什么用 Shape

在 SwiftUI 里,Shape 是最底层的绘制单位 。它的 path(in:) 方法允许我们在给定的矩形范围内,自己决定如何绘制路径。

你可以理解成:给了我们一张画布(rect),我们要在上面用"画笔"按顺序描出路径。

  • move(to:) 就是把画笔移动到某个点(不画线)。
  • addLine(to:) 是画直线。
  • addArc(...) 是画圆弧。
  • closeSubpath() 是闭合路径,自动连回起点。

这样拼起来,就能绘制出任何你想要的形状。

完整代码

less 复制代码
import SwiftUI

struct RoundedTopRectangle: Shape {
    func path(in rect: CGRect) -> Path {
        let width = rect.width
        let height = rect.height
        let cornerRadius: CGFloat = 24

        var path = Path()

        // 1. 起点:从左上角圆弧的下方开始 (0, cornerRadius)
        path.move(to: CGPoint(x: 0, y: cornerRadius))

        // 2. 左上角圆弧:180° → 270°
        path.addArc(
            center: CGPoint(x: cornerRadius, y: cornerRadius),
            radius: cornerRadius,
            startAngle: .degrees(180),
            endAngle: .degrees(270),
            clockwise: false
        )

        // 3. 顶部水平线:连接到右上角圆弧的起点
        path.addLine(to: CGPoint(x: width - cornerRadius, y: 0))

        // 4. 右上角圆弧:270° → 360°
        path.addArc(
            center: CGPoint(x: width - cornerRadius, y: cornerRadius),
            radius: cornerRadius,
            startAngle: .degrees(270),
            endAngle: .degrees(360),
            clockwise: false
        )

        // 5. 右侧直线到底部
        path.addLine(to: CGPoint(x: width, y: height))

        // 6. 底部直线到左下角
        path.addLine(to: CGPoint(x: 0, y: height))

        // 7. 左侧直线回到圆弧起点
        path.addLine(to: CGPoint(x: 0, y: cornerRadius))

        // 8. 闭合路径
        path.closeSubpath()

        return path
    }
}

角度怎么理解?

在 SwiftUI 里,角度是数学坐标系的标准方向

  • 0° = 水平方向(正右)
  • 90° = 垂直方向(正上)
  • 180° = 水平方向(正左)
  • 270° = 垂直方向(正下)

所以:

  • 左上角圆弧,我们要从左往下画:180° → 270°
  • 右上角圆弧,我们要从下往右画:270° → 360°

这就是为什么要用这些角度范围。

效果展示

VStack 里放一个 RoundedTopRectangle

css 复制代码
struct ContentView: View {
    var body: some View {
        VStack {
            Spacer()
            RoundedTopRectangle()
                .fill(Color.blue)
                .frame(height: 300)
                .shadow(radius: 5)
        }
        .edgesIgnoringSafeArea(.bottom)
    }
}

运行效果:

  • 底部是一个蓝色矩形
  • 顶部两个角是圆角,底部两个角是直角
  • 非常适合做 Bottom Sheet、播放器控制面板、支付弹窗等 UI。

改进思路

  1. 参数化圆角

    目前 cornerRadius 写死为 24,可以改成一个参数:

    swift 复制代码
    struct RoundedTopRectangle: Shape {
        var cornerRadius: CGFloat = 24
        func path(in rect: CGRect) -> Path { ... }
    }
  2. 只控制某些角

    如果你想要"底部圆角,顶部直角",只需要调整绘制的顺序,把圆弧放到底部。

  3. 支持可配置的四角圆角

    甚至可以写成一个更通用的 CustomRoundedRectangle,传入 [topLeft: true, topRight: false, ...] 这样的布尔配置,来决定哪些角是圆角。

总结

  • .cornerRadius() 方便,但太"平均"。
  • Shape 给了我们精细的控制,可以定义任何奇怪的矩形。
  • 理解 Path + 坐标 + 角度,你就能自由画出想要的图形。

自定义 Shape 是 SwiftUI 的隐藏宝藏,玩明白之后,不仅可以做圆角矩形,还能画波浪、曲线、对角线圆角等各种有趣的 UI。

相关推荐
开心就好20253 小时前
UniApp开发应用多平台上架全流程:H5小程序iOS和Android
后端·ios
开心就好20255 小时前
免 Xcode 的 iOS 开发新选择?聊聊一款更轻量的 iOS 开发 IDE kxapp 快蝎
后端·ios
恋猫de小郭9 小时前
Apple 的 ANE 被挖掘,AI 硬件公开,宣传的 38 TOPS 居然是"数字游戏"?
前端·人工智能·ios
东坡肘子12 小时前
OpenClaw 不错,但我好像没有那么需要 -- 肘子的 Swift 周报 #125
人工智能·swiftui·swift
忆江南1 天前
iOS 深度解析
flutter·ios
没有故事的Zhang同学1 天前
05-主题|事件响应者链@iOS-应用场景与进阶实践
ios
FeliksLv1 天前
尝试给Lookin 支持 MCP
ios
没有故事的Zhang同学1 天前
01-研究系统框架@Web@iOS | JavaScriptCore 框架:从使用到原理解析
ios
CocoaKier3 天前
苹果谷歌商店:如何监控并维护用户评分评论
ios·google·apple
iOS日常3 天前
iOS设备崩溃日志获取与查看
ios·xcode