Quartz 2D CGPattern学习笔记
CGPattern是在图形上下文中重复绘制的一系列绘制操作。你可以像使用颜色一样使用图案。当使用CGPattern进行绘制时,Quartz将页面划分为一组图案单元格,每个单元格的大小为CGPattern初始化时bounds的大小,并使用您提供的回调来绘制每个单元格。下图为一个简单的CGPattern绘制的图案:
白色部分为图形绘制的上下文 ,带蓝色边框的区域为CGPattern绘制的单元格,它是根据指定规则平铺在图形上下文中的。它总是从上下文的左上角(坐标系原点为左上角时)开始绘制,然后按照一定规则平铺满整个图形上下文。
上面图案的绘制具体代码如下:
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return } context.saveGState() // 保存上下文状态,以便绘制完图案后恢复上下文原来的状态(即:这里保存之前的状态) // 初始化一个回调函数
// 参数
// 1:版本号,一般传0即可;
// 2:一个绘制图案的函数(该函数有两个参数,1:CGPattern初始化时第一个参数info;2:绘制图案的上下文)
// 3:释放CGPattern初始化时第一个参数info指针的回调函数
var callbacks = CGPatternCallbacks(version: 0, drawPattern: { (info, ctx) in // 这里获取CGPattern初始化时传入的图案单元格的size(注意:这里不能用takeRetainedValue(),这样会导致info资源回收,而在下次回调中报错)
let size = Unmanaged<NSValue>.fromOpaque(info!).takeUnretainedValue().cgSizeValue
let bounds = CGRect(origin: .zero, size: size)
// 设置画笔宽度
ctx.setLineWidth(2)
// 设置画笔颜色
ctx.setStrokeColor(UIColor.red.cgColor)
// 设置填充色
ctx.setFillColor(UIColor.green.cgColor)
// 添加一个矩形框
ctx.addRect(bounds)
// 绘制边框并填充颜色
ctx.drawPath(using: .fillStroke)
}) { info in
// 需要在这里将CGPattern初始化时传入info指针内存回收
Unmanaged<NSValue>.fromOpaque(info!).release()
} // 图案单元格的间隙
let space: CGFloat = 10
// 当前上下文中,水平方向上单元格的个数(用于计算单元格的size)
let cellCount: CGFloat = 5
let drawBounds = bounds // 当前上下文画布矩形
// 计算图案单元格尺寸
let cellSize = (drawBounds.width - space * (cellCount - 1)) / cellCount
let size = CGSize(width: cellSize, height: cellSize)
// 初始化CGPattern
// 参数
// 1:用于向绘制函数中传递的info,这里我们将图案的单元格size作为info(这里使用passRetained,是为了保持NSValue的引用计数)
// 2:设置图案单元格的尺寸
// 3:突然单元格的变换矩阵
// 4:单元格水平方向的间距
// 5:单元格垂直方向的间距
// 6:平铺模式
// 7:图案单元格是否为彩色的(当我们绘制的突然需要颜色时,设置为true)
// 8:绘制图案的回调
guard let pattern = CGPattern(info: Unmanaged<NSValue>.passRetained(NSValue(cgSize: size)).toOpaque(),
bounds: CGRect(x: 0, y: 0, width: size.width, height: size.height),
matrix: .identity,
xStep: size.width + space,
yStep: size.height + space,
tiling: .noDistortion,
isColored: true,
callbacks: &callbacks) else {
print("pattern create fail")
return
}
// 创建颜色空间,采用patternBaseSpace初始化(注意:如果isColored=true,这里一般设置为nil即可)
guard let colorSpace = CGColorSpace(patternBaseSpace: nil) else {
print("colorSpace create fail")
return
}
// 设置填充的颜色空间(这里必须设置,否则图案将无法正常绘制填充色)
context.setFillColorSpace(colorSpace)
var alpha: CGFloat = 1
// 设置填充图案CGPattern,用于填充绘制。第二个参数:指定了图案绘制中用到的颜色透明度
context.setFillPattern(pattern, colorComponents: &alpha)
// 调用填充绘制,这里会采用上面的pattern进行绘制
context.fill(drawBounds)
context.restoreGState() // 恢复上下文状态
}
具体细节已在代码注释中说明,但是这里我要着重说明一下xStep和yStep这两个参数。我们用一张图来理解一下:
从图中可知,xStep是距离前一个图案单元格水平原点的距离,yStep是距离前一个图案单元格垂直原点的距离。因此,如果xStep小于单元格的宽度,那么前后两个单元格在水平方向将会重叠,yStep同样如此。
我们可以在回调函数中任意绘制我们的图案,不局限在绘制矩形或线条,向下面这样:
下面是具体绘制的部分代码:
var callbacks = CGPatternCallbacks(version: 0, drawPattern: { (info, ctx) in // 这里获取CGPattern初始化时传入的图案单元格的size(注意:这里不能用takeRetainedValue(),这样会导致info资源回收,而在下次回调中报错)
let size = Unmanaged<NSValue>.fromOpaque(info!).takeUnretainedValue().cgSizeValue // 设置填充色
ctx.setFillColor(UIColor.red.cgColor) let psize = min(size.width, size.height)
var r: Double, theta: Double
r = 0.8 * psize / 2
theta = 2 * Double.pi * (2.0 / 5.0)
ctx.translateBy(x: psize/2, y: psize/2)
ctx.move(to: .init(x: 0, y: r))
for k in 1..<5 {
ctx.addLine(to: .init(x: r * sin(Double(k) * theta), y: r * cos(Double(k) * theta)))
}
ctx.closePath()
ctx.fillPath()
}) { info in
// 需要在这里将CGPattern初始化时传入info指针内存回收
Unmanaged<NSValue>.fromOpaque(info!).release()
}
Quartz 2D CGPattern学习笔记的更多相关文章
- Quartz 2D编程指南(1) - 概览
Quartz 2D编程指南是论坛会员德鲁伊翻译的国外的Quartz 2D一系列学习资料,供大家参考 Quartz 2D是一个二维图形绘制引擎,支持iOS环境和Mac OS X环境.我们可以使用Quar ...
- Quartz学习笔记:集群部署&高可用
Quartz学习笔记:集群部署&高可用 集群部署 一个Quartz集群中的每个节点是一个独立的Quartz应用,它又管理着其他的节点.这就意味着你必须对每个节点分别启动或停止.Quartz集群 ...
- Quartz学习笔记:基础知识
Quartz学习笔记:基础知识 引入Quartz 关于任务调度 关于任务调度,Java.util.Timer是最简单的一种实现任务调度的方法,简单的使用如下: import java.util.Tim ...
- 我的Android进阶之旅------>Android中编解码学习笔记
编解码学习笔记(一):基本概念 媒体业务是网络的主要业务之间.尤其移动互联网业务的兴起,在运营商和应用开发商中,媒体业务份量极重,其中媒体的编解码服务涉及需求分析.应用开发.释放license收费等等 ...
- NGUI学习笔记(一)UILabel介绍
来个前言: 作为一个U3D程序员,自然要写一写U3D相关的内容了.想来想去还是从UI开始搞起,可能这也是最直观同时也最重要的部分之一了.U3D自带的UI系统,也许略坑,也没有太多介绍的价值,那么从今天 ...
- Theano 学习笔记(一)
Theano 学习笔记(一) theano 为什么要定义共享变量? 定义共享变量的原因在于GPU的使用,如果不定义共享的话,那么当GPU调用这些变量时,遇到一次就要调用一次,这样就会花费大量时间在数据 ...
- html5学习笔记一
HTML5学习笔记 <video>标记:定义视频,Ogg.MPEG4.WebM三种格式 <video src=”movie.ogg” controls=”controls”> ...
- iOS 2D绘图 (Quartz 2D) 概述
本篇博客原文地址:http://blog.csdn.net/hello_hwc?viewmode=list 由于自己的项目需要,从网络上下载了许多关于绘制图形的demo,只是用在自己的项目中,很多地方 ...
- Android动画学习笔记-Android Animation
Android动画学习笔记-Android Animation 3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中 ...
- opencv学习笔记(三)基本数据类型
opencv学习笔记(三)基本数据类型 类:DataType 将C++数据类型转换为对应的opencv数据类型 OpenCV原始数据类型的特征模版.OpenCV的原始数据类型包括unsigned ch ...
随机推荐
- Java期末测试
会议预约管理信息系统(50分) 1.项目背景: 会议是企业进行决策.协商的重要组织形式,是企业日常办公处理事务的重要手段,是办公流程中不可缺少的重要环节,作为企业,如何有效的进行会议组织,管理 ...
- elasticsearch之使用正则表达式自定义分词逻辑
一.Pattern Analyzer简介 elasticsearch在索引和搜索之前都需要对输入的文本进行分词,elasticsearch提供的pattern analyzer使得我们可以通过正则表达 ...
- Cobalt Strike 之:提权
郑重声明: 本笔记编写目的只用于安全知识提升,并与更多人共享安全知识,切勿使用笔记中的技术进行违法活动,利用笔记中的技术造成的后果与作者本人无关.倡导维护网络安全人人有责,共同维护网络文明和谐. Co ...
- Binary &Op是什么
前言 在并行开发时我们经常会用到Pstream::gather()函数或是全局函数reduce()或者其他,需要输入参数Binary &Op,本篇主要讨论Binary &Op是什么 t ...
- 解读C#编程中最容易忽略7种编写习惯!
编程时犯错是必然的,我们来解读一下编程中最容出现的错误 1.拼接字符串 在C#编程中,字符串类型的处理是比较容易出错的地方,在.NET Framework中,字符串是一个不可变的类型,当一个字符串被修 ...
- js 操作符 —— 位操作符详解
这篇文章不讲一元运算符,也就是 + .-. *. /. =. ||. &&. !这些. 位运算符是在数字底层(即表示数字的32个数位)进行操作的. 有符号整数使用 32 位的前 31 ...
- Hive数仓基础
架构图: 组成:SQL语句到任务执行需要经过解释器,编译器,优化器,执行器 解释器:调用语法解释器和语义分析器将SQL语句转换成对应的可执行的java代码或业务代码 编译器:将对应的java代码转 ...
- 关于centos8yum源失效问题
[CentOS8遇到错误]Error: Failed to download metadata for repo 'powertools'... 原因: CentOS Linux 8 已于 2021 ...
- undrop-for-innodb
undrop是一款针对mysql innodb的数据恢复工具,通过扫描文件或磁盘设备,然后解析innodb数据页进而恢复丢失的数据,对于drop.truncate以及文件损坏都很有帮助.本文介绍dro ...
- 不用PyScript,网页端运行的Python编辑器
原文:https://lwebapp.com/zh/python-online 需求 有小伙伴可能听说过 PyScript,知道了Python可以通过打包成wasm运行在浏览器端了,这样做一些需要Py ...