Quartz 提供了两种不透明的数据类型来创建渐变CGShading 和 CGGradient,你可以使用其中任何一个来创建轴向或径向渐变。

轴向渐变:沿着一个轴方向线性渐变

径向渐变:一个点为原型,指定半径的范围内辐射渐变

我们用两个图来理解一下这两个概念:

1、轴向渐变(轴:沿着左上角 到 右下角)

2、径向渐变(以中心点为圆心,向半径为50的四周方向辐射渐变)

CGShadingCGGradient之间的关系,以及使用的不同。

CGGradientCGShading的子集,CGGradient封装了对CGShading的渐变实现函数,因此CGGradient使用起来会比CGShading简单。

下面介绍一下两者的不同:

CGGradient CGShading
使用同一个CGGradient实例可以同时绘制轴向渐变和径向渐变 在绘制轴向渐变和径向渐变时,需要分别实例化不同的对象
在绘制时创建渐变的几何形状 在创建对象时创建渐变的几何形状
Quartz自动完成计算渐变中每个点的颜色变化 你需要提供一个回调函数,来计算渐变中每个点的颜色
使用简单,定义两个点和对应的颜色即可 需要在回调函数中计算,使用比较麻烦

我们先来介绍一下CGGradient的使用,它使用起来很简单,我们通过它来实现上面介绍的轴向渐变和径向渐变的例子

实例化方法:

// 参数
// 1、颜色空间
// 2、指定渐变中的颜色(需要与locations个数对应,每组颜色值为rgba(根据颜色空间决定),例如:1,0,0,1,表示一个红色,透明度为1)
// 3、指定渐变的过度位置(值范围:[0,1],0:几何开始的位置;1:几何结束的位置;0.5:几何中间的位置)
init?(colorSpace space: CGColorSpace,
colorComponents components: UnsafePointer<CGFloat>,
locations: UnsafePointer<CGFloat>?, count: Int) // 参数
// 1、颜色空间
// 2、指定渐变中的颜色(需要与locations个数对应,每组颜色值为:CGColor)
// 3、指定渐变的过度位置(值范围:[0,1],0:几何开始的位置;1:几何结束的位置;0.5:几何中间的位置)
init?(colorsSpace space: CGColorSpace?, colors: CFArray, locations: UnsafePointer<CGFloat>?)

具体实现代码:

override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return } // 初始化颜色空间,有两种方法,一般采用第一种(下面两个颜色空间是一样的)
// 方法1:
let space = CGColorSpaceCreateDeviceRGB()
// 方法2:
// let space = CGColorSpace(name: CGColorSpace.sRGB)!
// 设置渐变的过度位置,范围:[0, 1]
var locations: [CGFloat] = [0, 0.5, 1]
// 设置过度的颜色组件集合,因为我们的颜色空间是RGB,因此每组的颜色值为rgba,即:[r, g, b, a]
// 注意:每组的颜色是在数组中连续的,即:[r,g,b,a, r,g,b,a, r,g,b,a]
var colors: [CGFloat] = [1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1]
// 初始化渐变对象CGGradient
guard let gradient = CGGradient(colorSpace: space,
colorComponents: &colors,
locations: &locations,
count: locations.count) else {
print("gradient create fail")
return
}
// 绘制轴向渐变
// 参数:
// 1、渐变对象实例
// 2、渐变的几何开始点坐标
// 3、渐变的几何结束点坐标
// 4、绘制选项,包括:
// (1)drawsBeforeStartLocation:在几何开始点的位置之前的空间部分(超出边界不绘制),用第一个颜色值扩散颜色
// (2)drawsAfterEndLocation:在几何结束点的位置之后的空间部分(超出边界不绘制),用最后一个颜色值扩散颜色
context.drawLinearGradient(gradient,
start: .init(x: 0, y: 0),
end: .init(x: bounds.width, y: bounds.height),
options: .drawsBeforeStartLocation) // 绘制径向渐变
// 参数:
// 1、渐变对象实例
// 2、渐变开始的圆心点坐标
// 3、渐变开始的圆半径(辐射半径)
// 4、渐变结束的圆心点坐标
// 5、渐变结束的圆半径(辐射半径)
// 6、绘制选项,跟上面的一样
// context.drawRadialGradient(gradient,
// startCenter: .init(x:bounds.width/2, y:bounds.height/2),
// startRadius: 10,
// endCenter: .init(x: bounds.width/2, y: bounds.height/2),
// endRadius: 50,
// options: .drawsBeforeStartLocation) }

代码中注释介绍的已经很详细了,这里我给大家着重介绍一下options参数。我们看下图便一目了然:

红色区域:是渐变开始点绘制之前的颜色填充部分。

蓝色区域:是渐变结束点绘制之后的颜色填充部分。

options选项决定在渐变的开始点之前 还是 结束点之后 的区域内进行颜色填,填充的颜色色值为:

1、开始点之前绘制(drawsBeforeStartLocation):填充色为颜色数组中第一个颜色值

2、结束点之后绘制(drawsAfterEndLocation):填充色为颜色数组中最后一个颜色值

下面是两种options的轴向渐变的绘图结果:(径向渐变也是如此)

drawsBeforeStartLocation:(开始点前采用红色填充,结束点后无填充)

drawsAfterEndLocation:(开始点前无填充,结束点后采用蓝色填充)

接下来介绍CGShading的使用,它在绘制渐变时用起来比CGGradient要复杂,因为需要我们自己来计算渐变过程中每个点的颜色。使用时涉及到以下几个关键对象:

CGFunctionCallbacks:用于提供计算渐变过程中每个点的颜色的回调函数,这就是我们实现渐变的关键点。

CGFunction:为上面的回调函数设置初始值,以及相关值的范围的对象。

CGShading:定义渐变的规则,例如:颜色空间、从几何区域的哪里开始渐变到哪里结束渐变等。

实现轴向渐变与径向渐变的实例化方法不一样,下面是对应这两种实现的实例化方法:

// 初始化轴向渐变对象
init?(axialSpace space: CGColorSpace,
start: CGPoint,
end: CGPoint,
function: CGFunction,
extendStart: Bool,
extendEnd: Bool) // 初始化径向渐变对象
init?(radialSpace space: CGColorSpace,
start: CGPoint,
startRadius: CGFloat,
end: CGPoint,
endRadius: CGFloat,
function: CGFunction,
extendStart: Bool,
extendEnd: Bool)

下面是一个利用CGShading实现轴向渐变的具体实现的demo,代码中注释写的很详细,大家可以参考。

override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return } // 初始化一个回调函数,用于计算每个点的颜色值
// 参数:
// 1、CGFunction初始化传入的第一个参数info(指针类型)
// 2、输入值
// 3、输出值
// 4、释放info指针的回调函数
var callback = CGFunctionCallbacks(version: 0) { info, inVal, outVal in let numberOfComponents: Int
if let info = info {
// 注意这里不能用takeRetainedValue()取值,否则会提前释放info的内存,导致下次访问info时出错
numberOfComponents = Unmanaged<NSNumber>.fromOpaque(info).takeUnretainedValue().intValue
}else{
numberOfComponents = 0
} // 渐变开始的颜色值
let startColor: [CGFloat] = [1, 0, 0, 1]
// 渐变结束的颜色值
let endColor: [CGFloat] = [0, 1, 0, 1] let aInVal = inVal.pointee // 获取每次迭代的输入值,下面指定了输入值范围在[0, 1],因此aInVal的值范围在[0, 1]
var out = outVal // 下面指定了输出值包含的分量个数为numberOfComponents(即:out中应返回numberOfComponents个元素)
for i in 0..<numberOfComponents { // 注意:下面指定了输出值中每个分量(每个元素)的值范围[0, 1],因此这里的计算结果不要超出这个范围
if aInVal < 0.5 {
// 计算渐变开始点到中间点区间的颜色分量值
out.pointee = startColor[i] * (0.8 - aInVal)
}else{
// 计算渐变中间点到结束点区间的颜色分量值
out.pointee = endColor[i] * (aInVal - 0.2)
}
out = out.successor()
} } releaseInfo: { info in
guard let info = info else { return }
// 释放info指针内存
Unmanaged<NSNumber>.fromOpaque(info).release()
} // 初始化颜色空间
let space = CGColorSpaceCreateDeviceRGB() // 获取通道颜色分量的个数,这里要加一个alpha
let numberOfComponents = space.numberOfComponents + 1 // rgb + a
// 指定上面callback中输入值的递增迭代次数,这里设置1,即:输入值会从domain的范围内,迭代一次(0, 0.0.00167, ..., 1)终止
// 如果设置为2,会迭代两次:第一次(0, 0.0.00167, ..., 1),第二次(0, 0.0.00167, ..., 1)
let domainDimension = 1
// 输入值的变化范围,这里指定从0到1,即输入值会根据渐变的开始点到结束点的变化,从0开始递增到1,如(0, 0.0.00167, ..., 1)
// 如果设置为[1, 10],则输入值会从1开始递增到10
let domain: [CGFloat] = [0, 1] // 注意:这里的范围必须是递增的
// 指定上面callback中输出值包含的分量个数,如果为4,那么上面callback的输出值out指针应该包含4个分量值(即:rgba)。
// 可以理解为输出值数组的size
let rangeDimension = numberOfComponents
// 输出值每个分量的范围,即r值的范围[0, 1],g值的范围[0, 1], b值的范围[0, 1], a值的范围[0, 1]
// 可以理解为输出值数组中每个元素值的取值范围
let range: [CGFloat] = [0, 1, 0, 1, 0, 1, 0, 1] // 数组下标0和1代表第一个值的范围,一次类推
guard let function = CGFunction(info: Unmanaged<NSNumber>.passRetained(NSNumber(integerLiteral: numberOfComponents)).toOpaque(),
domainDimension: domainDimension,
domain: domain,
rangeDimension: rangeDimension,
range: range,
callbacks: &callback) else {
print("function create fail")
return
} // 渐变的开始点坐标
let start = CGPoint(x: bounds.width/4, y: bounds.height/4)
// 渐变的结束点坐标
let end = CGPoint(x: bounds.width * 3 / 4, y: bounds.height * 3 / 4) // 指定渐变开始点之前的区域是否执行填充颜色,相当于使用CGGradient绘制渐变时,调用drawLinearGradient方法时设置的:drawsBeforeStartLocation
let extendStart = true
// 指定渐变结束点之后的区域是否执行填充颜色,相当于使用CGGradient绘制渐变时,调用drawLinearGradient方法时设置的:drawsAfterEndLocation
let extendEnd = true
// 初始化一个轴向渐变实例CGShading
guard let shading = CGShading(axialSpace: space,
start: start,
end: end,
function: function,
extendStart: extendStart,
extendEnd: extendEnd) else {
print("shading create fail")
return
}
// 绘制渐变
context.drawShading(shading)
}

代码中注释介绍的已经很详细了,这里就不详细说明了,下面是实现的效果:

径向渐变的实现也是类似的,只不过是CGShading的实例化方法不一样。下面是采用CGShading实现的径向渐变的效果:

Quartz 2D CGGradient与CGShading实现渐变的绘制的更多相关文章

  1. iOS 2D绘图详解(Quartz 2D)之阴影和渐变(Shadow,Gradient)

    前言:这个系列写道这里已经是第五篇了,本文会介绍下阴影和渐变的基础知识,以及一些基本的Demo Code展示,应该还会有两篇,介绍下Bitmap绘制以及Pattern等知识. Shadow shado ...

  2. iOS - Quartz 2D 二维绘图

    1.Quartz 2D 简介 Quartz 2D 属于 Core Graphics(所以大多数相关方法的都是以 CG 开头),是 iOS/Mac OSX 提供的在内核之上的强大的 2D 绘图引擎,并且 ...

  3. 1.0 Quartz 2D 简介

    本文并非最终版本,如有更新或更正会第一时间置顶,联系方式详见文末 如果觉得本文内容过长,请前往本人 “简书”   Quartz2D须知:   (1)Quartz 2D是苹果官方的二维绘图引擎,同时支持 ...

  4. iPhone之Quartz 2D系列--图形上下文(2)(Graphics Contexts)

    以下几遍关于Quartz 2D博文都是转载自:http://www.cocoachina.com/bbs/u.php?action=topic&uid=38018 iPhone之Quartz ...

  5. Quartz 2D - 图形上下文(Graphics Contexts)

    一个Graphics Context表示一个绘制目标.它包含绘制系统用于完成绘制指令的绘制参数和设备相关信息.Graphics Context定义了基本的绘制属性,如颜色.裁减区域.线条宽度和样式信息 ...

  6. 使用Quartz 2D擦除图片

    Quartz 2D 是一个强大的二位图像绘制引擎,在开发中如果遇到需要高度自定义的控件,我们就可能需要用Core Graphics进行绘制. 这几天一同事开发一个聊天中的一个子模块,A 画一幅图,然后 ...

  7. Quartz 2D编程指南(2) - 图形上下文

    一个Graphics Context表示一个绘制目标.它包含绘制系统用于完成绘制指令的绘制参数和设备相关信息.Graphics Context定义了基本的绘制属性,如颜色.裁减区域.线条宽度和样式信息 ...

  8. iOS 2D绘图 (Quartz 2D) 概述

    本篇博客原文地址:http://blog.csdn.net/hello_hwc?viewmode=list 由于自己的项目需要,从网络上下载了许多关于绘制图形的demo,只是用在自己的项目中,很多地方 ...

  9. iOS 2D绘图详解(Quartz 2D)之概述

    前言:最近在研究自定义控件,由于想要彻底的定制控件的视图还是要继承UIView,虽然对CALayer及其子类很熟练,但是对Quartz 2D这个强大的框架仍然概念模棱两可.于是,决定学习下,暂定7篇文 ...

  10. iPhone之Quartz 2D系列--编程指南(1)概览

    以下几遍关于Quartz 2D博文都是转载自:http://www.cocoachina.com/bbs/u.php?action=topic&uid=38018 iPhone之Quartz ...

随机推荐

  1. Vue 01 简介

    1 官网    1)英文官网: https://vuejs.org/ 2)中文官网: https://cn.vuejs.org/ 2 介绍与描述 1) 动态构建用户界面的渐进式 JavaScript ...

  2. 编译报错,提示:This dependency was not found:* vue-editor-bridge

    前端代码引入了: 1 import func from 'vue-editor-bridge'; 工具自动填充,导致引入上述JS去掉重新编译,问题解决

  3. SRE:如何提高报警有效性?

    为什么要提升<报警有效性> 过多的报警会让负责人麻木 过多的报警会增加短信和电话的成本 提升根因定位效率 如何定义<报警有效性> 不漏报 不误报 不重报 不延报 如何量化 MT ...

  4. TCP/IP协议(1): IP 地址和寻址方式 —— IP 协议的基础

    TCP/IP协议(1): IP 地址和寻址方式 -- IP 协议的基础 最近在重学计算机网络,给自己立一个 flag,有感而发的时候写关于 TCP/IP 协议栈的系列博客. IP 地址 IP 地址(I ...

  5. JZOJ 3448.公路维护

    \(\text{Problem}\) 1.询问区间最小值是否大于 \(0\) 2.区间加(可正可负) 3.区间取 \(\max\) 如果某个数经过操作后小于等于 \(0\),以后的操作就不会再影响这个 ...

  6. 欧拉函数和遗忘自动机 SX 的故逝

    欧拉函数 \(\varphi(n)\) 定义为小于 \(n\) 与 \(n\) 互质的数字,炒个例子,\(\varphi(10) = 4\),因为 \(1,3,7,9\) 与 \(10\) 互质. 怎 ...

  7. 系统提权之:Unix 提权

    郑重声明: 本笔记编写目的只用于安全知识提升,并与更多人共享安全知识,切勿使用笔记中的技术进行违法活动,利用笔记中的技术造成的后果与作者本人无关.倡导维护网络安全人人有责,共同维护网络文明和谐. 系统 ...

  8. Layui 表单元素考到页面样式不生效

    表单元素必须要标记在表单里面(calss="layui-form") 例如: <div class="layui-form"> <input ...

  9. nodejs npm错误Error:UNKNOWN:unknown error,mkdir 'D:\Develop\nodejs\node_global'at Error(可行)

    错误原因 在设置npm的cache和predix时,因为使用参考其他教程照抄,而没有修改为本机对应路径,本机上并没有这个地址,因此造成了错误. npm config set cache "D ...

  10. GIS若干相关的概念

    投影 常用的投影坐标系: CGCS2000_3_Degree_GK_CM_111E CGCS2000_3_Degree_GK_Zone_37 CGCS2000_GK_Zone_19 Beijing_1 ...