View — Shapes
This article is extracted from the chat log with AI. Please identify it with caution.

在 SwiftUI 中,Shapes 是定义用户界面中几何形状的核心工具之一。通过使用 Shape 协议或已有的 Shape 类型,可以轻松创建、组合、裁剪和装饰各种形状。SwiftUI 提供了一系列内置形状工具,如 RectangleEllipseCapsule 等,并允许开发者通过实现 Shape 协议来自定义形状。Shape 的灵活性使其在创建按钮背景、图像裁剪、容器框架和装饰性 UI 元素中非常重要。


1. 什么是 Shape 体系结构? #

1.1 Shape 协议 #

Shape 是 SwiftUI 中的一个协议。遵循这个协议的类型必须实现以下方法:

protocol Shape {
    func path(in rect: CGRect) -> Path
}

核心要点:

  • path(in rect:) 方法:用来描述形状的路径(Path),并接收一个 CGRect 来定义绘制的区域。
  • Path 是用于描述几何图形的 SwiftUI 数据结构,允许用绘点、线段、曲线等方式定义自定义形状。

1.2 Shape 的核心行为 #

  • 绘制形状:使用 SwiftUI 的 Canvas,形状被绘制在指定的绘图框 CGRect 中。
  • 填充形状:形状可以使用颜色、图像或渐变进行填充(.fill())。
  • 描边(边框):可以使用 .stroke() 定义边框颜色和宽度。
  • 裁剪:可以使用形状作为视图的蒙版(clipShape())。

2. 内置 Shape 类型 #

SwiftUI 提供了一系列内置的形状,这些形状可以立即使用,并通过 Frame、填充或修饰符调整外观:

名称描述用例
Rectangle一个基本矩形。用作背景、容器、装饰元素。
RoundedRectangle带圆角的矩形,通过 cornerRadius 控制圆角半径。按钮、标签、圆角背景。
Circle一个标准的圆形(宽高等于 Frame 时)。欢迎页面的头像、圆形裁剪区域。
Ellipse自动适应 Frame 尺寸的椭圆形(长短轴根据比例调整)。装饰背景、裁剪、进度条形状。
Capsule圆角矩形,类似于药丸形状,宽高比不等时表现为椭圆。创建按钮、Tag 样式化背景。
Triangle没有内置 Triangle,可通过实现 Shape 协议定义一个三角形。装饰元素、指示性标志。
Polygon需通过自定义 Shape 创建多边形(四边形、五边形等)。创建图表、动画图形组件。
Path自由绘制的用于定义复杂路径的形状。创建高度定制化的形状,例如波浪、星形、多边形等。

3. Shape 的常见操作 #

通过修改器,SwiftUI 提供了一些常见的形状操作支持,如填充、描边、添加渐变、阴影等。

3.1 填充颜色 #

struct FillExample: View {
    var body: some View {
        Circle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
    }
}

3.2 描边(Stroke) #

struct StrokeExample: View {
    var body: some View {
        RoundedRectangle(cornerRadius: 20)
            .stroke(Color.red, lineWidth: 5) // 描边颜色和宽度
            .frame(width: 150, height: 100)
    }
}

3.3 渐变填充 #

struct GradientFillExample: View {
    var body: some View {
        Ellipse()
            .fill(
                LinearGradient(
                    gradient: Gradient(colors: [.blue, .green]),
                    startPoint: .leading, // 渐变从左
                    endPoint: .trailing   // 渐变到右
                )
            )
            .frame(width: 200, height: 100)
    }
}

3.4 阴影 #

struct ShadowExample: View {
    var body: some View {
        Capsule()
            .fill(Color.orange)
            .frame(width: 150, height: 50)
            .shadow(color: .black.opacity(0.3), radius: 10, x: 5, y: 5) // 添加阴影
    }
}

3.5 裁剪蒙版(Clip Shape) #

Shape 可以用作裁剪视图的蒙版。

struct ClipShapeExample: View {
    var body: some View {
        Image("exampleImage")
            .resizable()
            .scaledToFit()
            .frame(width: 200, height: 200)
            .clipShape(Circle()) // 使用 Circle 裁剪图片
    }
}

4. 自定义 Shape #

通过实现 Shape 协议,开发者可以绘制自定义形状。

4.1 绘制一个三角形 #

struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        Path { path in
            path.move(to: CGPoint(x: rect.midX, y: rect.minY))  // 顶点
            path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) // 右下
            path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) // 左下
            path.closeSubpath() // 闭合路径
        }
    }
}

struct TriangleExample: View {
    var body: some View {
        Triangle()
            .fill(Color.blue)
            .frame(width: 150, height: 150)
    }
}

4.2 创建一个多边形 #

通过可配置的 sideCount 参数,我们可以实现灵活的多边形(五边形、六边形等)。

struct Polygon: Shape {
    let sides: Int

    func path(in rect: CGRect) -> Path {
        guard sides >= 3 else { return Path() }  // 至少需要三边

        let center = CGPoint(x: rect.midX, y: rect.midY)
        let radius = min(rect.width, rect.height) / 2
        let angleIncrement = 2 * .pi / CGFloat(sides)

        return Path { path in
            for i in 0..<sides {
                let angle = CGFloat(i) * angleIncrement - .pi / 2
                let point = CGPoint(
                    x: center.x + radius * cos(angle),
                    y: center.y + radius * sin(angle)
                )
                if i == 0 {
                    path.move(to: point)
                } else {
                    path.addLine(to: point)
                }
            }
            path.closeSubpath() // 闭合路径
        }
    }
}

struct PolygonExample: View {
    var body: some View {
        Polygon(sides: 5) // 五边形
            .stroke(Color.purple, lineWidth: 2)
            .frame(width: 200, height: 200)
    }
}

4.3 addArc #

在 Apple 的开发框架(如 ​Core Graphics​ 或 ​SwiftUI)中,path.addArc 是一个用于向绘图路径(CGPath 或 Path)中添加圆弧的方法。它允许你通过指定圆心、半径、起始角度和结束角度来精确绘制圆弧或圆形。


核心参数解析

以 Swift 的 Core Graphics 为例,方法的典型定义如下:

// Core Graphics 中的 CGPath 方法
func addArc(
    center: CGPoint,      // 圆心坐标
    radius: CGFloat,      // 圆弧半径
    startAngle: CGFloat,  // 起始角度(弧度)
    endAngle: CGFloat,    // 结束角度(弧度)
    clockwise: Bool       // 是否顺时针绘制
)

关键细节​:

  1. 角度单位​:参数 startAngle 和 endAngle 使用弧度制,而非角度制。

    • 例如:0 弧度对应右侧(3点钟方向),π/2 对应顶部(12点钟方向)。
  2. 坐标系方向​:

    • 在 iOS/macOS 的绘图坐标系中,​Y 轴向下延伸​(与数学坐标系相反)。
    • 因此,clockwise: true 在屏幕上可能表现为逆时针方向​(需特别注意)。

代码示例

在 Core Graphics 中绘制一个 ​90° 圆弧​(从右侧到顶部):

let path = CGMutablePath()
path.addArc(
    center: CGPoint(x: 100, y: 100),  // 圆心坐标
    radius: 50,                       // 半径 50
    startAngle: 0,                    // 起始角度:右侧(0弧度)
    endAngle: .pi/2,                  // 结束角度:顶部(π/2弧度)
    clockwise: true                   // "顺时针"(实际为逆时针方向)
)

// 绘制到图形上下文
let context = UIGraphicsGetCurrentContext()
context?.addPath(path)
context?.strokePath()

常见用途

  1. 绘制扇形或圆环​:

    • 结合 move(to: center) 和 addLine(to: center),闭合路径可形成扇形。
    • 通过设置 lineWidth 和 stroke,可绘制空心圆环。
  2. 动态进度条​:

    • 根据进度值动态计算 endAngle,绘制圆弧表示加载进度。
  3. 复杂图形的一部分​:

    • 结合直线(addLine)、贝塞尔曲线(addCurve)等,构建自定义形状。

注意事项

  1. 角度方向陷阱​:
    若发现圆弧方向与预期相反,尝试将 clockwise 参数取反(false → true 或反之)。

  2. 闭合路径​:
    若需绘制闭合图形(如扇形),需手动添加线条回到圆心或调用 closePath()

  3. SwiftUI 中的等效方法​:
    在 SwiftUI 的 Path 中,使用 addArc 或 arc 方法,语法略有不同:

    Path { path in
        path.addArc(
            center: CGPoint(x: 100, y: 100),
            radius: 50,
            startAngle: .degrees(0),
            endAngle: .degrees(90),
            clockwise: true
        )
    }
    

数学原理

圆弧的数学定义基于极坐标系:

  • 圆心​:确定圆弧位置。
  • 半径​:确定圆弧大小。
  • 起始/结束角度​:控制圆弧覆盖的角度范围。

通过组合多个圆弧和路径操作,可实现复杂的图形绘制需求。


5. 组合 Shapes #

5.1 叠加多个 Shape #

ZStack 可以用来叠加多个形状。

struct CombinedShapesExample: View {
    var body: some View {
        ZStack {
            Circle().fill(Color.blue).frame(width: 150, height: 150)
            RoundedRectangle(cornerRadius: 20)
                .fill(Color.orange)
                .frame(width: 100, height: 100)
        }
    }
}

5.2 自定义复合效果 #

通过形状的组合,可以绘制更加复杂的图形。


6. Shapes 的应用场景 #

  1. 背景设计: 创建按钮背景、卡片背景、标签等样式化元素。
  2. 裁剪效果: 用于裁剪图片或视图内容。
  3. 动态图形: 结合 @State 和动画实现有趣的交互式效果。
  4. 图表组件: 绘制饼图、柱状图、折线图等。
  5. 装饰性元素: 增强 UI 丰富性,例如分隔器、波浪边框等。
  6. 状态显示: 创建进度条、加载指示器等。

总结 #

SwiftUI 的 Shapes 体系是一个强大的视觉构建工具:

  1. 基础形状:提供 RectangleCircleEllipse 等易用形状,用于常见的 UI 元素。
  2. 高度可定制性:通过实现 Shape 协议,开发者可以自由绘制自定义形状。
  3. 多样化修饰器:提供渐变、描边、阴影等修饰器丰富视觉效果。
  4. 与动画结合Shape 与 SwiftUI 的动画特性(如 withAnimationGeometryEffect)无缝兼容,支持动态变化。

无论是简单的 UI 元素还是复杂的装饰内容,Shapes 都能够提供强大的支持。

本文共 2588 字,创建于 Jan 8, 2025
相关标签: SwiftUI, Xcode, ByAI