SwiftUI绘画视图Canvas
SwiftUI绘画视图Canvas

SwiftUI绘画视图Canvas

Canvas 是 SwiftUI 中引入的一种强大的绘图视图,允许Swift 代码高效地绘制自定义的图形、路径和视觉效果。它的功能类似于 Core Graphics,但使用了 SwiftUI 风格的声明式语法。

基本结构

1、Canvas 初始化器

Canvas { context, size in
    // 在这里绘图
}
.frame(width: 200, height: 200)

context:表示绘图上下文,用于绘制内容(如路径、文本、图像)。

size:表示 Canvas 的尺寸,绘制内容应基于此动态适配。

常见修饰符

1)frame(width:height:);

2)background(_:);

3)foregroundStyle(_:);

4)drawingGroup():启用 GPU 渲染和抗锯齿。

常用绘制方法

1、绘制路径

context.stroke(Path(ellipseIn: CGRect(x: 10, y: 10, width: 100, height: 100)), with: .color(.blue), lineWidth: 2)

2、填充路径

context.fill(Path(rect), with: .color(.green))

3、绘制图像

context.draw(Image("photo"), in: CGRect(x: 0, y: 0, width: 100, height: 100))

4、绘制文本

let text = Text("Hello").font(.title)
context.draw(text, at: CGPoint(x: 50, y: 50))

5、绘制带样式的图像

context.stroke(path, with: .linearGradient(Gradient(colors: [.red, .blue]), startPoint: .zero, endPoint: CGPoint(x: 100, y: 100)), lineWidth: 4)

使用示例

1、绘制圆形

Canvas { context, size in
    let rect = CGRect(origin: .zero, size: size)
    let circle = Path(ellipseIn: rect)
    context.fill(circle, with: .color(.blue))
}
.frame(width: 100, height: 100)

2、绘制渐变矩形

Canvas { context, size in
    let rect = CGRect(origin: .zero, size: size)
    let path = Path(rect)
    let gradient = Gradient(colors: [.red, .orange])
    context.fill(path, with: .linearGradient(gradient,
                                             startPoint: .zero,
                                             endPoint: CGPoint(x: size.width, y: size.height)))
}
.frame(width: 200, height: 100)

3、绘制文字

Canvas { context, size in
    let text = Text("Hello Canvas")
        .font(.system(size: 20))
    context.draw(text, at: CGPoint(x: size.width / 2, y: size.height / 2))
}
.frame(width: 200, height: 100)

4、绘制圆形和直线

Canvas { context, size in
    // 绘制背景
    context.fill(
        Path(CGRect(origin: .zero, size: size)),
        with: .color(.blue.opacity(0.2))
    )
    
    // 绘制一个圆形
    let circleRect = CGRect(x: size.width / 4, y: size.height / 4, width: size.width / 2, height: size.height / 2)
    context.stroke(Path(ellipseIn: circleRect), with: .color(.red), lineWidth: 5)
    
    // 绘制一条对角线
    let linePath = Path { path in
        path.move(to: CGPoint(x: 0, y: 0))
        path.addLine(to: CGPoint(x: size.width, y: size.height))
    }
    context.stroke(linePath, with: .color(.green), lineWidth: 3)
}
.frame(width: 300, height: 300)

5、动态内容示例:旋转圆圈

struct ContentView: View {
    @State private var rotation: Double = 0.0

    var body: some View {
        TimelineView(.animation) { timeline in
            let date = timeline.date.timeIntervalSinceReferenceDate
            let rotation = date.truncatingRemainder(dividingBy: 2) * 180
            
            Canvas { context, size in
                let radius = min(size.width, size.height) / 2 - 20
                let center = CGPoint(x: size.width / 2, y: size.height / 2)
                
                let startAngle = Angle(degrees: rotation)
                let endAngle = Angle(degrees: rotation + 270)
                
                var path = Path()
                path.addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
                
                context.stroke(path, with: .color(.orange), lineWidth: 10)
            }
        }
        .frame(width: 300, height: 300)
    }
}

值得注意的是,Canvas 的绘制内容是基于一次性的静态上下文执行的,并不会动态监听状态更新。为了解决这个问题,代码中需要明确地触发视图的更新,因此使用TimelineView视图,使得视图每次刷新时 Canvas 都重新计算绘制内容。

6、使用 TimelineView 创建动态时钟

TimelineView 提供了基于时间的更新机制,可以与 Canvas 配合实现动态时钟。

import SwiftUI

struct ClockView: View {
    var body: some View {
        TimelineView(.animation) { timeline in
            Canvas { context, size in
                let now = timeline.date
                let calendar = Calendar.current
                let second = Double(calendar.component(.second, from: now))
                let minute = Double(calendar.component(.minute, from: now))
                let hour = Double(calendar.component(.hour, from: now) % 12)
                
                let center = CGPoint(x: size.width / 2, y: size.height / 2)
                let radius = min(size.width, size.height) / 2 - 20
                
                // 秒针
                let secondAngle = Angle.degrees((second / 60) * 360 - 90)
                let secondEnd = CGPoint(
                    x: center.x + cos(Double(secondAngle.radians)) * radius,
                    y: center.y + sin(Double(secondAngle.radians)) * radius
                )
                context.stroke(
                    Path { path in
                        path.move(to: center)
                        path.addLine(to: secondEnd)
                    },
                    with: .color(.red), lineWidth: 2
                )
                
                // 其他指针可以类似绘制...
            }
        }
        .frame(width: 300, height: 300)
    }
}

使用场景

1、动画和动态内容

与 SwiftUI 的动画系统结合良好,适合创建动态效果。

2、自定义绘图

如果需要绘制自定义图形、仪表盘、进度条等,Canvas 是理想选择。

3、高性能绘图

底层使用 Metal 支持,性能优异,适合实时更新的内容。

4、可使用TimelineView

TimelineView 配合,可以创建时间驱动的动画内容。

和Shape的关系

1、Shape 是 SwiftUI 中用来描述几何形状的协议类型。只需要提供路径,它就可以:

1)自动参与布局(支持 .frame、.padding 等);

2)自动适应颜色、动画;

3)与 SwiftUI 的 fill(), stroke() 等配合使用。

struct MyShape: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.addEllipse(in: rect)
        return path
    }
}

MyShape()
    .fill(Color.blue)
.frame(width: 100, height: 100)

2、Canvas 是一种底层绘图视图,可以直接在一个区域中绘制 Path、Image、Text 等内容。

它更像是 SwiftUI 中的 NSView.draw(_:) 或者 Core Graphics 的替代者,提供自定义绘图功能。

可以实现:

1)使用 GraphicsContext 在屏幕上自由绘制;

2)绘制多个对象、分层绘图、使用滤镜、合成模式等;

3)动态响应状态和动画(比如配合 TimelineView)。

Canvas { context, size in
    var path = Path()
    path.addRect(CGRect(origin: .zero, size: size))
    context.fill(path, with: .color(.blue))
}
.frame(width: 100, height: 100)

3、Shape和Canvas对比

Shape可以绘制几何形状、支持SwiftUI动画,属于View视图类型,自动适配布局系统,不适合复杂绘图(多个图层)。

Canvas可以自由绘图(路径、图像、文本等),支持图层合成、滤镜、阴影、透明度,属于View视图类型,但需要自己控制绘图大小,适合复杂绘图。

总结

Canvas 是 SwiftUI 中用于自定义绘图的现代工具,提供了高效、直观的绘图方式,支持动画和时间驱动的内容。无论是绘制静态图形还是动态内容,都可以很好地满足需求。

相关文章

1、SwiftUI实时更新内容的TimelineView视图:https://fangjunyu.com/2024/12/14/swiftui%e5%ae%9e%e6%97%b6%e6%9b%b4%e6%96%b0%e5%86%85%e5%ae%b9%e7%9a%84timelineview%e8%a7%86%e5%9b%be/

2、Apple图像渲染和计算框架Metal:https://fangjunyu.com/2024/11/17/apple%e5%9b%be%e5%83%8f%e6%b8%b2%e6%9f%93%e5%92%8c%e8%ae%a1%e7%ae%97%e6%a1%86%e6%9e%b6metal/

3、SwiftUI绘制自定义形状的Path:https://fangjunyu.com/2024/12/16/swiftui%e7%bb%98%e5%88%b6%e8%87%aa%e5%ae%9a%e4%b9%89%e5%bd%a2%e7%8a%b6%e7%9a%84path/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注