Shadow

Shadow(阴影) 的目的是为了使UI更有立体感,如图

iOS画阴影 苹果阴影部分怎么画_数组

shadow 主要有三个影响因素

x off-set 决定阴影沿着X的偏移量
y off-set 决定阴影沿着y的偏移量
blur value 决定了阴影的边缘区域是不是模糊的

其中不同的blur效果的图

iOS画阴影 苹果阴影部分怎么画_颜色值_02

注意:

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);
}

效果

iOS画阴影 苹果阴影部分怎么画_颜色空间_03


Gradient 渐变色

渐变无非就是从一种颜色逐渐变换到另一种颜色,Quartz 2D提供了两种渐变的模型。

Quartz提供了两个不透明数据odgago创建渐变:CGShadingRef 和 CGGradientRef.我们可以使用任何一个来创建轴向(axial)或径向(radial)渐变。一个 渐变是从一个颜色到另外一个颜色的填充。

一个轴向渐变(也称为线性渐变)沿着两个端点连接的轴线渐变。所有位于垂直于轴线的的某条线上的点都具有相同的颜色值。

一个径向渐变也是沿着两个端点连接的轴线渐变,不过路径通常由两个圆来定义。

 

1> axial gradient 线性渐变(轴向渐变),使用的时候设置好两个顶点的颜色 (也可以设置中间过渡色)

例如:

轴向渐变由橙色向黄色渐变 在这个例子中渐变轴相对于远点倾斜了四十五度角。

iOS画阴影 苹果阴影部分怎么画_颜色值_04

Quartz 也允许我们设置一系列的颜色值和位置值,以沿着轴来创建更复杂的轴向渐变。如下图所示,起始点的颜色设置为红色(可以看一下Quartz 的坐标系 零点默认就是左下角),结束点的颜色是紫罗兰色。同时,在轴上有5个位置,它们的颜色分别设置为橙 黄 绿 蓝 和 靛蓝。我们可以把它看成沿着同一轴线的六段连续的线性渐变。另外轴线的角度 有我们设置的两个端点来定义。

iOS画阴影 苹果阴影部分怎么画_颜色值_05

2> radial gradient(径向渐变)

这种模式的渐变允许 一个圆到另一个圆的渐变

如下图所示,他从一个小的明亮的红色圆渐变到一个大小黑色的圆,

iOS画阴影 苹果阴影部分怎么画_颜色值_06

 

 

我们可以把一个圆放置到一个径向渐变中来创建各种形状。如果一个圆是另一个的一部分或者完全在另一个的外面,则Quartz创建了圆锥和一个圆柱。径向渐变的一个通常用法就是创建一个球体阴影

一个单一的点(半径为0的圆)位于一个大圆以内。

iOS画阴影 苹果阴影部分怎么画_数组_07

CGShading 和 CGGradient对象的对比

我们有两个对象类型用于创建渐变,你可能想知道哪一个更好用,看了下面你就会知道了。

CGshadingRef这个不透明的数据类型给我们更多的控制权,以确定如何计算每个端点的颜色,在我们创建CGShading对象之前,我们必须创建一个CGFunction对象(CGFunctionRef),这个对象定义了一个用于计算渐变色的函数。写一个自定义的函数让我们创建平滑的渐变。

当我们创建一个CGShading对象时,我们指定其是轴向还是径向,除了计算函数以外,我们还需要提供一个颜色空间。起始点 结束点 或者是半径,这取决于绘制轴向还是径向渐变。在绘制时,我们只是简单的传递CGShading对象以及绘制上下文给CGContextDrawShading函数,Quartz为渐变上的每个点调用渐变计算函数。

一个CGGRadient对象是CGShading对象的子集,其更容易使用,CGGradientRef不透明类型抑郁作用,因为Quartz在渐变的每一点上计算颜色值。我们不需要提供一个渐变计算函数。当创建好一个渐变对象的时候,我们提供一个位置和颜色的数组。quartz使用对应的颜色值来计算每个梯度的渐变。我们可以使用单一的气势与结束点来设置一个渐变对象。或者提供一组断电来创建一个多颜色渐变的效果。使用CGShading对象可以提供多于两个位置的能力。

当我们创建一个CGGradient对象时,我们需要设置一个颜色空间、位置、和每个位置对应的颜色值。当使用一个渐变对象绘制上下文时,我们指定Quartz是绘制一个轴向还是径向渐变。在绘制时,我们指定开始结束点或半径,这取决于我们是绘制轴向还是径向渐变。而CGShading的几何形状是在创建时定义的,而不是绘制时。

扩展渐变端点外部的颜色

当我们创建一个渐变时,我们可以选择使用纯色来填充渐变端点外部的空间。Quartz使用使用渐变边界上的颜色作为填充颜色。我们可以扩展渐变起点、终点或两端的颜色。我们可以扩展使用CGShading对象或CGGradient对象创建的轴向或径向渐变。

图演示了一个轴向渐变,它扩展了起点和终点两侧的区域。图片中的线段显示了渐变的轴线。我们可以看到,填充颜色与起点和终点的颜色是对应的。

iOS画阴影 苹果阴影部分怎么画_颜色值_08

通过 这两种渐变的嵌套使用 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);
}

效果

iOS画阴影 苹果阴影部分怎么画_颜色值_09

径向渐变

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);

iOS画阴影 苹果阴影部分怎么画_数组_10


 

使用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);