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。

相关推荐
tiantian_cool6 小时前
Claude Code 四大核心技能使用指南
ios
冰淇淋真好吃10 小时前
iOS实现 WKWebView 长截图的优雅方案
ios
前端不太难15 小时前
Flutter / RN / iOS,在长期维护下的性能差异本质
flutter·ios
搜狐技术产品小编20232 天前
精通 UITableViewDiffableDataSource——从入门到重构的现代 iOS 列表开发指南
ios·重构
tangweiguo030519872 天前
SwiftUI 状态管理完全指南:从 @State 到 @EnvironmentObject
ios
Digitally2 天前
如何轻松地将文件从 PC 传输到 iPhone
ios·iphone
iosTiov2 天前
当IPA遇见信任:解密ios生态中“签名”的真正力量
ios·团队开发·苹果签名·稳定
游戏开发爱好者82 天前
如何使用 AppUploader 提交上传 iOS 应用
android·ios·小程序·https·uni-app·iphone·webview
和沐阳学逆向2 天前
iOS 18 越狱教程:palera1n + 巨魔安装全流程
ios·巨魔商店·ios越狱·ios18越狱
ii_best3 天前
安卓/ios脚本开发辅助工具按键精灵横纵坐标转换教程
android·开发语言·ios·安卓