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。

相关推荐
JarvanMo9 小时前
2025 年真正有效的 App Store 优化(ASO)
前端·ios
熊大与iOS19 小时前
iOS 长截图的完美实现方案 - 附Demo源码
android·算法·ios
songgeb1 天前
DiffableDataSource in iOS
ios·swift
2501_916008891 天前
uni-app iOS 应用版本迭代与上架实践 持续更新的高效流程
android·ios·小程序·https·uni-app·iphone·webview
白玉cfc1 天前
【iOS】折叠cell
ios·objective-c
2501_915909061 天前
uni-app iOS 性能监控与调试全流程:多工具协作的实战案例
android·ios·小程序·https·uni-app·iphone·webview
他们都不看好你,偏偏你最不争气1 天前
【iOS】MVC架构
前端·ios·mvc·objective-c·面向对象
willlzq1 天前
KeyPath:从OC到Swift有哪些改变?
ios