Shadow
Shadow(阴影) 的目的是为了使UI更有立体感,如图
shadow 主要有三个影响因素
x off-set 决定阴影沿着X的偏移量
y off-set 决定阴影沿着y的偏移量
blur value 决定了阴影的边缘区域是不是模糊的
其中不同的blur效果的图
注意:
shadow也是和绘制状态相关的,意味着如果仅仅绘制一个subPath的shadow,注意save和restore
相关函数
CGContextSetShadow
CGContextSetShadowWithColor//唯一区别是设置了阴影颜色
参数
context 绘制画板
offset阴影偏移量,参考context的坐标系
blur 非负数,决定阴影的模糊程度
示例代码
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
//填充边框 当然你可以用layer设置
CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillRect(context, rect);
CGContextStrokeRect(context, rect);
CGContextAddArc(context, 100, 100, 50, 0, M_2_PI, 0);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 3.0);
CGContextSetShadow(context, CGSizeMake(4.0, 4.0), 1.0);
//CGContextSetShadowWithColor(context, CGSizeMake(4.0, 4.0), 1.0, [UIColor blueColor].CGColor);
CGContextStrokePath(context);
}
效果
Gradient 渐变色
渐变无非就是从一种颜色逐渐变换到另一种颜色,Quartz 2D提供了两种渐变的模型。
Quartz提供了两个不透明数据odgago创建渐变:CGShadingRef 和 CGGradientRef.我们可以使用任何一个来创建轴向(axial)或径向(radial)渐变。一个 渐变是从一个颜色到另外一个颜色的填充。
一个轴向渐变(也称为线性渐变)沿着两个端点连接的轴线渐变。所有位于垂直于轴线的的某条线上的点都具有相同的颜色值。
一个径向渐变也是沿着两个端点连接的轴线渐变,不过路径通常由两个圆来定义。
1> axial gradient 线性渐变(轴向渐变),使用的时候设置好两个顶点的颜色 (也可以设置中间过渡色)
例如:
轴向渐变由橙色向黄色渐变 在这个例子中渐变轴相对于远点倾斜了四十五度角。
Quartz 也允许我们设置一系列的颜色值和位置值,以沿着轴来创建更复杂的轴向渐变。如下图所示,起始点的颜色设置为红色(可以看一下Quartz 的坐标系 零点默认就是左下角),结束点的颜色是紫罗兰色。同时,在轴上有5个位置,它们的颜色分别设置为橙 黄 绿 蓝 和 靛蓝。我们可以把它看成沿着同一轴线的六段连续的线性渐变。另外轴线的角度 有我们设置的两个端点来定义。
2> radial gradient(径向渐变)
这种模式的渐变允许 一个圆到另一个圆的渐变
如下图所示,他从一个小的明亮的红色圆渐变到一个大小黑色的圆,
我们可以把一个圆放置到一个径向渐变中来创建各种形状。如果一个圆是另一个的一部分或者完全在另一个的外面,则Quartz创建了圆锥和一个圆柱。径向渐变的一个通常用法就是创建一个球体阴影
一个单一的点(半径为0的圆)位于一个大圆以内。
CGShading 和 CGGradient对象的对比
我们有两个对象类型用于创建渐变,你可能想知道哪一个更好用,看了下面你就会知道了。
CGshadingRef这个不透明的数据类型给我们更多的控制权,以确定如何计算每个端点的颜色,在我们创建CGShading对象之前,我们必须创建一个CGFunction对象(CGFunctionRef),这个对象定义了一个用于计算渐变色的函数。写一个自定义的函数让我们创建平滑的渐变。
当我们创建一个CGShading对象时,我们指定其是轴向还是径向,除了计算函数以外,我们还需要提供一个颜色空间。起始点 结束点 或者是半径,这取决于绘制轴向还是径向渐变。在绘制时,我们只是简单的传递CGShading对象以及绘制上下文给CGContextDrawShading函数,Quartz为渐变上的每个点调用渐变计算函数。
一个CGGRadient对象是CGShading对象的子集,其更容易使用,CGGradientRef不透明类型抑郁作用,因为Quartz在渐变的每一点上计算颜色值。我们不需要提供一个渐变计算函数。当创建好一个渐变对象的时候,我们提供一个位置和颜色的数组。quartz使用对应的颜色值来计算每个梯度的渐变。我们可以使用单一的气势与结束点来设置一个渐变对象。或者提供一组断电来创建一个多颜色渐变的效果。使用CGShading对象可以提供多于两个位置的能力。
当我们创建一个CGGradient对象时,我们需要设置一个颜色空间、位置、和每个位置对应的颜色值。当使用一个渐变对象绘制上下文时,我们指定Quartz是绘制一个轴向还是径向渐变。在绘制时,我们指定开始结束点或半径,这取决于我们是绘制轴向还是径向渐变。而CGShading的几何形状是在创建时定义的,而不是绘制时。
扩展渐变端点外部的颜色
当我们创建一个渐变时,我们可以选择使用纯色来填充渐变端点外部的空间。Quartz使用使用渐变边界上的颜色作为填充颜色。我们可以扩展渐变起点、终点或两端的颜色。我们可以扩展使用CGShading对象或CGGradient对象创建的轴向或径向渐变。
图演示了一个轴向渐变,它扩展了起点和终点两侧的区域。图片中的线段显示了渐变的轴线。我们可以看到,填充颜色与起点和终点的颜色是对应的。
通过 这两种渐变的嵌套使用 Quartz 2D能绘制出非常漂亮的图形
渐变的两种绘制模型
CGShading -使用这种数据类型需要自己定义CFFunction来计算每一个点的渐变色,较为复杂,但是能够灵活的绘制。
CGGradient-使用这种数据类型只需要制定两个顶点的颜色,以及绘制模式,其余的Quartz会给绘制,但是渐变的数学模型不灵活。
使用CGGradient对象:
一个CGGradient 对象是一个渐变的抽象定义 他简单的质地昂了颜色值和位置,但没有指定任何几何形状,我们可以在轴向和径向几何形状中使用这个对象,作为一个轴向定义,CGGradient对象可能比CGShading对象更容易重用。没有讲任何几何形状存储在CGGradient对象中。这样允许我们使用相同的颜色方案来绘制不同的几何图形,而不需要为多个图形创建多个CGGadient对象。
因为Quartz为我们计算渐变,使用一个CGGradient对象来创建和绘制一个渐变则更直接,只需要以下几步:
1>创建一个CGGradient对象,提供一个颜色空间,一个饱含两个或更多颜色组件的数组,一个包含两个或多个位置的数组,和两个数组中元素的个数。
2>使用CGContextDrawLinearGradient或者CGContentDrawRadialGradient绘制。
3>释放CGGradient对象
一个位置是一个值区间在0.0到1.0之间的CGFloat值,他指定了沿着渐变的轴线的标准化距离值0.0的值指定轴线的起点,1.0的值指定了轴线的重点。其他值指定了一个距离的比例。最低限度情况下,Quartz使用两个位置值。如果我们传递NULL值作为位置数组参数,则Quartz使用0作为第一个位置,1作为第二个位置。
每个颜色的颜色组件的数目取决于颜色空间。对于离屏绘制,我们使用一个RGB颜色空间。因为Quartz使用alpha来绘制,每个离屏颜色都有四个组件—红、绿、蓝和alpha。所以,对于离屏绘制,我们提供的颜色组件数组的元素的数目必须是位置数目的4倍。Quartz的RGBA颜色组件可以在0.0到1.0之间改变。
代码
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
//使用CGGradient绘制
CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB();
size_t num_of_locations = 2;
//注意每个位置对应一个颜色
CGFloat locations[2] = {0.0,1.0};
CGFloat components[8] = {1.0,0.0,0.0,1.0,//红色
0.0,1.0,0.0,1.0};//绿色
CGGradientRef gradient = CGGradientCreateWithColorComponents(deviceRGB, components, locations, num_of_locations);
CGPoint startPoint = CGPointMake(0, 0);
CGPoint endPoint = CGPointMake(rect.size.width, rect.size.height);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGColorSpaceRelease(deviceRGB);
CGGradientRelease(gradient);
}
效果
径向渐变
CGContextRef context = UIGraphicsGetCurrentContext();
//颜色空间
CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB();
//位置数组和颜色数组包含元素的个数
size_t num_of_locations = 2;
CGFloat locations[2] = {0.0,1.0};
CGFloat components[8] = {0.0,0.0,1.0,1.0,//白色
0.0,1.0,0.0,1.0};//黑色
CGGradientRef gradient = CGGradientCreateWithColorComponents(deviceRGB, components, locations, num_of_locations);
CGPoint startCenter = CGPointMake(100, 100);
CGPoint endCenter = CGPointMake(125, 125);
CGFloat startRadius = 0.0;
CGFloat endRadius = 50.0;
CGContextDrawRadialGradient(context, gradient, startCenter, startRadius, endCenter, endRadius, 0);
CGColorSpaceRelease(deviceRGB);
CGGradientRelease(gradient);
使用CGShading对象
我们通过调用函数CGShadingCreateAxial或者CGShadingCreateRadial创建一个CGShading对象来设置一个渐变,调用这些函数需要提供以下参数。
1.CGColorSpace 对象:颜色空间
2.起始点和终点。对于轴向渐变,有轴向的起始点和和终点的坐标,对于径向渐变,有起始圆和终点圆的中心坐标
3.用于定义渐变区域的圆的启示半径和终止半径
4.一个CGFunction对象,可以通过CGFunctionCreate函数来获取。这个回调例程必须返回绘制到特定点的颜色值。
5.一个布尔值用于指定是否适用纯色来绘制起始点和终点的扩展区域
我们提供给CGShading创建函数的CGFunction对象包含一个回调结构体,及Quartz需要实现这个回调的所有信息。也许设置CGShasing对象的最棘手的部分是创建CGFunction对象。当我们调用CGFunctionCreate函数时,我们提供以下参数:
CGFunctionRef _Nullable CGFunctionCreate (
void * _Nullable info,
size_t domainDimension,
const CGFloat * _Nullable domain,
size_t rangeDimension,
const CGFloat * _Nullable range,
const CGFunctionCallbacks * _Nullable callbacks
);
看到这就很头疼啊?当然,CGGradient对象足矣满足大部分时候的需求,不过有空的话还是耐心下来看看吧。我们先看看参数
info 用来传递到callback的数据(就是指向回调所需要的数据的指针),注意他的生命周期可能不只是方法的生命周期
domainDimesion 输入的数量,quart中,就是1。(回调输入值的个数,Quartz要求回调携带一个输入值)
domain 一组数据 确定输入的有效间隔,在Quartz中是0到1,0表示开始,1表示结束 (一个浮点数的数组。Quartz只会提供数组中的一个元素给回调函数。一个转入值的范围是0(渐变的开始点的颜色)到1(渐变的结束点的颜色)。)
rangDimension 输出的数量:(回调提供的输出值的数目,对于每一个输入值,我们的回调必须为每个颜色组件提供一个值,以及一个alpha值来指定透明度,颜色组件值由Quartz提供的颜色空间来解释,并提供给CGShading创建函数。例如如果我们使用RGB颜色空间,则我们提供4作为输出值(R,G,B,A)的数目)
rang 输出的有效间隔 (一个浮点数的数组,用于指定每个颜色组件的值及alpha值)
callbacks 用来计算的实际方法 (一个回调数据结构,包含结构体的版本(设置为0)、生成颜色组件值的回调、一个可选的用于释放回调中info参数表示的数据。)格式如下 格式如下void myCalculateShadingValues (void *info, const CGFloat *in, CGFloat *out)
在创建CGShading对象后,如果需要我们可以设置额外的裁减操作。然后调用CGContextDrawShading函数来使用渐变来绘制上下文的裁减区域。当调用这个函数时,Quartz调用回调函数来获取从起点到终点这个范围内的颜色值。
当不再需要CGShading对象时,我们调用CGShadingRelease来释放它。
首先设置CGFunction对象来计算颜色值
我们可以以我们想要的方式来计算颜色值,我们的颜色计算函数包含以下三个参数:
1.void *info 这个值可以为NULL 或者是一个指向传递给CGShading创建函数的数据
2.const CGFloat *in Quartz传递in数组给回调。数组中的值必须在为CGFunction对象定义的输入值范围内
3.我们的回调函数传递out数组给Quartz。它包含用于颜色空间中每个颜色组件的元素及一个alpha值。输出值应该在CGFunction对象中定义的输出值的范围内
static void myCalculateShadingValues (void *info,
const CGFloat *in,
CGFloat *out)
{
CGFloat v;
size_t k, components;
static const CGFloat c[] = {1, 0, 0.5, 0 };
components = (size_t)info;
v = *in;
for (k = 0; k < components -1; k++)
*out++ = c[k] * v;
*out++ = 1;
}
这里的三个参数,函数很简单out的值(r,g,b,a)分别为(in*1,in*0.in*0.5,1)
创建一个CGFuction
static CGFunctionRef myGetFunction (CGColorSpaceRef colorspace) //1 {
size_t numComponents;
static const CGFloat input_value_range [2] = { 0, 1 };
static const CGFloat output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 }; static const CGFunctionCallbacks callbacks = { 0, //2
&myCalculateShadingValues,
NULL };
numComponents = 1 + CGColorSpaceGetNumberOfComponents (colorspace); //3
return CGFunctionCreate ((void *) numComponents, 1, input_value_range, numComponents, output_value_ranges, &callbacks);
}
其中,每一行分别为
以colorspace作为参数
定义callback函数
计算颜色域中的颜色组建的个数,例如RGB就是三个,然后加一,表示alpha通道
用CGShading绘制Axial Gradient
CGPoint startPoint,
endPoint;
CGFunctionRef myFunctionObject;
CGShadingRef myShading;
startPoint = CGPointMake(0,0.5);
endPoint = CGPointMake(1,0.5);
colorspace = CGColorSpaceCreateDeviceRGB();
myFunctionObject = myGetFunction (colorspace);
myShading = CGShadingCreateAxial (colorspace,
startPoint, endPoint,
myFunctionObject,
false, false)
CGContextDrawShading (myContext, myShading);
CGShadingRelease (myShading);
CGColorSpaceRelease (colorspace);
CGFunctionRelease (myFunctionObject);
用CGShading绘制Radial Gradient
callback
static void myCalculateShadingValues (void *info,
const CGFloat *in,
CGFloat *out)
{
size_t k, components;
double frequency[4] = { 55, 220, 110, 0 };
components = (size_t)info;
for (k = 0; k < components - 1; k++)
*out++ = (1 + sin(*in * frequency[k]))/2;
*out++ = 1; // alpha
}
CGPoint startPoint, endPoint;
CGFloat startRadius, endRadius;
startPoint = CGPointMake(0.25,0.3);
startRadius = .1;
endPoint = CGPointMake(.7,0.7);
endRadius = .25;
colorspace = CGColorSpaceCreateDeviceRGB();
myShadingFunction = myGetFunction (colorspace);
CGShadingCreateRadial (colorspace,
startPoint,
startRadius,
endPoint,
endRadius,
myShadingFunction,
false,
false)
CGContextDrawShading (myContext, shading);
CGShadingRelease (myShading);
CGColorSpaceRelease (colorspace);
CGFunctionRelease (myFunctionObject);