用最简单的方式让 SwiftUI 画一颗爱你的小红心

一个小目标

用 SwiftUI 画一颗爱你的小心心有多难呢?答案是:超简单。

这还不算完,我们随后还将讨论如何画一颗五彩跳动着的爱心,并献给小伙伴们作为伟大祖国的国庆献礼:

全部代码在 Xcode 26 中调试通过,在 iOS 26 里运行完美。

在本篇博文中,您将学到如下内容:

  • 一个小目标
  • 快使用 Charts!?你想不到吧?
  • 干正事:画一枚小心心
    *
    1. LinePlot
      1. 样式美化
      1. 坐标范围
  • 更进一步:画一颗跳动的心!
    • 1️⃣ 绘制心形路径
    • 2️⃣ 添加渐变与阴影
    • 3️⃣ 实现动画效果
    • 4️⃣ 启动动画
  • 总结

那还等什么呢?让我们马上开始"寻心"之旅吧! Let's go!!!;)


快使用 Charts!?你想不到吧?

iOS 16~17 中,SwiftUI 的 Charts 框架主要是通过 LineMarkBarMarkPointMark 等元素来绘制图表。

iOS 18 / SwiftUI 6.0 引入了更简洁的 LinePlot 类型,用于绘制线形图(折线图或连续曲线),更符合数据科学和函数的绘图场景。

它的核心特点是:

  • 函数式数据绑定:可以直接使用闭包提供 x、y 数据点,甚至可以实时生成曲线。
  • 自动处理平滑曲线 :相比 LineMarkLinePlot 可以直接支持平滑曲线(类似样条曲线),视觉更自然。
  • 支持 domain 指定 :可以指定 x 轴范围,例如 domain: 0...10,自动计算曲线采样点。
  • 类型安全:SwiftUI 的类型系统直接约束 x、y 的类型,不易出错。

下面我们来了解一下它的基本用法示例:

swift 复制代码
import SwiftUI
import Charts

struct SineWaveView: View {
    var body: some View {
        Chart {
            LinePlot(x: "x", y: "y", t: "t", domain: 0...2*Double.pi) { t in
                let x = t
                let y = sin(t)
                return (x: x, y: y)
            }
        }
        .frame(height: 300)
        .padding()
    }
}

让老夫来为宝子们解释一番吧:

  • x: "x"y: "y":绑定 x、y 数据字段。
  • domain: 0...2*π:指定绘图区间。
  • 闭包 { t in ... }:对每个 t 计算对应点 (x, y)
  • 生成的图是一个连续的折线或平滑曲线。

在 Charts 里使用 LinePlot 有下面这些优势:

优势 说明
简洁 不需要手动构造数据数组,直接通过闭包生成曲线
动态 可响应状态变化实时更新曲线
平滑 内置支持样条曲线、平滑化,适合函数绘图或时间序列
可扩展 支持设置样式、颜色、渐变、标记等

简单来说,LinePlot 就是 SwiftUI 6.0 为曲线绘图场景提供的"轻量化、高级化"折线图元素 ,相比以前需要手动生成数据的 LineMark,它的 API 更加直观、函数式,更适合数学曲线或实时数据流的绘图。

干正事:画一枚小心心

上面我们介绍了 LinePlot 类型,下面我们就来看看如何用它画一颗小心心吧。

我们的总体思路是:

使用 SwiftUI Charts 框架绘制一个红心形状,并在心形上添加渐变色、阴影效果,同时设置坐标轴范围和工具栏文字。

  • Chart :这是绘图容器,内部可以放不同的 Plot(这里是 LinePlot)。
  • LinePlot :用来绘制曲线,通过参数生成一系列点 (x, y)
  • lineStyle / foregroundStyle / shadow:用于美化曲线,设置线宽、渐变颜色、阴影。
  • chartXScale / chartYScale:手动设置坐标范围,使心形比例合适。

代码如下所示:

swift 复制代码
struct Heart: View {
    
    var body: some View {
        Chart {
            LinePlot(x: "x", y: "y", t: "t", domain: -.pi ... .pi) { t in
                let x = sqrt(2) * pow(sin(t), 3)
                let y = cos(t) * (2 - cos(t) - pow(cos(t), 2))
                return (x, y)
            }
            .lineStyle(StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
            .foregroundStyle(
                RadialGradient(
                    gradient: Gradient(colors: [.red, .pink]),
                    center: .center,
                    startRadius: 0,
                    endRadius: 200
                )
            )
            .shadow(color: .pink.opacity(0.6), radius: 15)
            .shadow(color: .red.opacity(0.4), radius: 30)
        }
        .chartXScale(domain: -3...3)
        .chartYScale(domain: -4...2)
        .toolbar {
            Text("大熊猫侯佩 @ \(Text("CSDN").foregroundStyle(.red))")
                .font(.headline)
                .foregroundStyle(.gray)
                .fixedSize()
                .padding(.horizontal)
        }
    }
}

对上面代码的关键部分,我们来简单解析一番:

1. LinePlot

swift 复制代码
LinePlot(x: "x", y: "y", t: "t", domain: -.pi ... .pi) { t in
    let x = sqrt(2) * pow(sin(t), 3)
    let y = cos(t) * (2 - cos(t) - pow(cos(t), 2))
    return (x, y)
}
  • 使用参数 t 生成心形曲线 (x, y)
  • x 坐标sqrt(2) * sin(t)^3
  • y 坐标cos(t) * (2 - cos(t) - cos(t)^2)
  • domain: -.pi ... .pi:t 的范围从 -π 到 π。

这是心形的数学公式参数化表示。

2. 样式美化

swift 复制代码
.lineStyle(StrokeStyle(lineWidth: 10, lineCap: .round, lineJoin: .round))
.foregroundStyle(
    RadialGradient(
        gradient: Gradient(colors: [.red, .pink]),
        center: .center,
        startRadius: 0,
        endRadius: 200
    )
)
.shadow(color: .pink.opacity(0.6), radius: 15)
.shadow(color: .red.opacity(0.4), radius: 30)
  • 线宽 10,端点和连接圆润。
  • 径向渐变从红到粉,让心形更有层次感。
  • 两层阴影增加立体感。

3. 坐标范围

swift 复制代码
.chartXScale(domain: -3...3)
.chartYScale(domain: -4...2)

手动设置 x 和 y 的显示范围,使心形在图中居中且不被裁剪。

最后总结一下:

  1. 数学公式生成心形坐标。
  2. LinePlot 绘制曲线。
  3. 渐变 + 阴影 + 线条样式美化曲线。
  4. 设置 坐标范围工具栏

是不是简单的不要不要的呢?

更进一步:画一颗跳动的心!

让我们再锦上添花,画一颗五彩跳动着的心吧。

实现代码分为两部分:

  1. HeartShape :用数学公式绘制心形路径(Shape)。
  2. PulsingRainbowHeartView :使用这个心形路径绘制 彩虹渐变、心跳动画、旋转渐变效果,并添加阴影。

整体效果是:一颗会跳动、颜色动态流动的彩虹心。

swift 复制代码
struct HeartShape: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        let scale = min(rect.width, rect.height) / 4.0
        let center = CGPoint(x: rect.midX, y: rect.midY)
        
        var firstPoint = true
        for t in stride(from: -Double.pi, through: Double.pi, by: 0.01) {
            let x = sqrt(2) * pow(sin(t), 3)
            let y = cos(t) * (2 - cos(t) - pow(cos(t), 2))
            
            let point = CGPoint(
                x: center.x + x * scale,
                y: center.y - y * scale
            )
            
            if firstPoint {
                path.move(to: point)
                firstPoint = false
            } else {
                path.addLine(to: point)
            }
        }
        
        path.closeSubpath()
        return path
    }
}

struct PulsingRainbowHeartView: View {
    @State private var beat = false
    @State private var hueRotation: Angle = .degrees(0)
    @State private var gradientShift = false
    
    var body: some View {
        HeartShape()
            .fill(
                LinearGradient(
                    gradient: Gradient(colors: gradientShift
                        ? [.red, .orange, .yellow, .green, .blue, .purple, .pink]
                        : [.pink, .purple, .blue, .green, .yellow, .orange, .red]
                    ),
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing
                )
            )
            .hueRotation(hueRotation) // 彩虹旋转
            .shadow(color: .pink.opacity(0.6), radius: 15)
            .shadow(color: .purple.opacity(0.4), radius: 30)
            .scaleEffect(beat ? 1.1 : 0.95) // 心跳缩放
            .animation(
                Animation.easeInOut(duration: 0.8).repeatForever(autoreverses: true),
                value: beat
            )
            .animation(
                Animation.linear(duration: 5).repeatForever(autoreverses: false),
                value: hueRotation
            )
            .animation(
                Animation.easeInOut(duration: 3).repeatForever(autoreverses: true),
                value: gradientShift
            )
            .onAppear {
                beat = true
                hueRotation = .degrees(360) // 开始旋转
                gradientShift.toggle()      // 开始流动切换渐变
            }
            .frame(width: 300, height: 300)
            .navigationTitle("一颗爱你的心永远转不停")
            .toolbar {
                Text("大熊猫侯佩 @ \(Text("CSDN").foregroundStyle(.red))")
                    .font(.headline)
                    .foregroundStyle(.gray)
                    .fixedSize()
                    .padding(.horizontal)
            }
    }
}

上面这段彩虹心形代码的绘制思路简单可以分为下面几个主要步骤:

1️⃣ 绘制心形路径

  • 使用 HeartShape 自定义 Shape
  • 用数学公式生成心形曲线 (x, y),并将其缩放、居中到给定矩形中。

2️⃣ 添加渐变与阴影

  • 给心形填充 线性渐变,支持彩虹色流动。
  • 添加 双层阴影,增强立体感。

3️⃣ 实现动画效果

  • 心跳缩放 :通过 scaleEffect 实现放大缩小动画。
  • 彩虹旋转 :使用 hueRotation 实现颜色循环。
  • 渐变切换:渐变方向或颜色流动动画。

4️⃣ 启动动画

  • 在视图出现时(onAppear)启动所有动画,让心形连续跳动和流动。

总结:先画心形 → 填充渐变和阴影 → 加动画 → 启动动画 → 美化视图

大功告成,打完收工,棒棒哒!💯

总结

在本篇博文中,我们讨论了如何在 SwiftUI 7(iOS 26)中用内置的框架极简画出一颗爱你的小心心,小伙伴们值得拥有哦。

感谢观赏,再会啦!8-)

相关推荐
HarderCoder5 小时前
Swift 初探:从变量到并发,一文带你零基础读懂官方 Tour
swift
HarderCoder6 小时前
SwiftUI Binding 深坑指南:为什么 `Binding(get:set:)` 会让你的视图疯狂重绘?
swift
QWQ___qwq1 天前
My Swift笔记
swift
小蕾Java2 天前
IDEA快速上手指南!
java·intellij-idea·swift
山顶夕景3 天前
【LLM】基于ms-Swift大模型SFT和RL的训练实践
大模型·微调·swift·强化学习
HarderCoder4 天前
SwiftUI redraw 机制全景解读:从 @State 到 Diffing
swift
pixelpilot4 天前
Nimble:让SwiftObjective-C测试变得更优雅的匹配库
开发语言·其他·objective-c·swift
大熊猫侯佩4 天前
张真人传艺:Swift 6.2 Actor 隔离协议适配破局心法
swiftui·swift·apple
Dream_Ji6 天前
Swift入门(二 - 基本运算符)
服务器·ssh·swift