美文网首页iOS动画及开源库iOS精进
控件渐变色的实现(二)—— Core Graphics实现

控件渐变色的实现(二)—— Core Graphics实现

作者: 刀客传奇 | 来源:发表于2017-08-08 00:04 被阅读0次

    版本记录

    版本号 时间
    V1.0 2017.08.07

    前言

    很多时候视图的颜色并不是单一的,需要渐变或者更炫的色彩,这里我就说一下控件渐变色的实现方法。希望能帮助大家。感兴趣的可以看我上一篇文章。
    1. 控件渐变色的实现(一)—— CAGradientLayer实现

    功能要求

    利用Core Graphics控件渐变色的实现。


    功能实现

    Core Graphics中有两个方法用于绘制渐变颜色:

    • CGContextDrawLinearGradient可以用于生成线性渐变。
    • CGContextDrawRadialGradient用于生成圆半径方向颜色渐变。

    函数可以自定义path,无论是什么形状都可以,原理都是用来做Clip,所以需要在CGContextClip函数前调用CGContextAddPath函数把CGPathRef加入到Context中。

    另外一个需要注意的地方是渐变的方向,方向是由两个点控制的,点的单位就是坐标。因此需要正确从CGPathRef中找到正确的点,方法当然有很多种看具体实现,下面方法中,我就是简单得通过调用CGPathGetBoundingBox函数,返回CGPathRef的矩形区域,然后根据这个矩形取两个点。

    1. 线性渐变

    还是直接看代码。

    #import "JJGradientGraphicVC.h"
    #import "Masonry.h"
    
    @interface JJGradientGraphicVC ()
    
    @end
    
    @implementation JJGradientGraphicVC
    
    #pragma mark - Override Base Function
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        self.title = @"线性渐变色";
        self.view.backgroundColor = [UIColor whiteColor];
        
        //创建CGContextRef
        UIGraphicsBeginImageContext(self.view.bounds.size);
        CGContextRef contextRef = UIGraphicsGetCurrentContext();
        
        //创建CGMutablePathRef
        CGMutablePathRef pathRef = CGPathCreateMutable();
        
        //绘制path
        CGRect rect = CGRectMake(50.0, 100.0, 300.0, 250.0);
        CGPathMoveToPoint(pathRef, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect));
        CGPathAddLineToPoint(pathRef, NULL, CGRectGetMaxX(rect), CGRectGetMinY(rect));
        CGPathAddLineToPoint(pathRef, NULL, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
        CGPathCloseSubpath(pathRef);
        
        //绘制渐变色
        [self drawLinearGradientWithContext:contextRef path:pathRef beginColor:[UIColor redColor].CGColor endColor:[UIColor greenColor].CGColor];
        
        //释放CGMutablePathRef
        CGPathRelease(pathRef);
        
        //从上下文中获取图像并显示
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
        [self.view addSubview:imageView];
    
        [imageView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.center.equalTo(self.view);
            make.width.equalTo(@300);
            make.height.equalTo(@250);
        }];
    }
    
    #pragma mark - Object Private Function
    
    - (void)drawLinearGradientWithContext:(CGContextRef)context
                                     path:(CGPathRef)path
                               beginColor:(CGColorRef)beginColor
                                 endColor:(CGColorRef)endColor;
    {
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGFloat locations[] = {0.0, 1.0};
        NSArray *colorArr = @[(__bridge id)beginColor, (__bridge id)endColor];
        CGGradientRef gradientRef = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colorArr, locations);
        CGRect pathRect = CGPathGetBoundingBox(path);
        
        //具体方向可根据需求修改
        CGPoint startPoint = CGPointMake(CGRectGetMinX(pathRect), CGRectGetMidY(pathRect));
        CGPoint endPoint = CGPointMake(CGRectGetMaxX(pathRect), CGRectGetMidY(pathRect));
        
        CGContextSaveGState(context);
        CGContextAddPath(context, path);
        CGContextClip(context);
        CGContextDrawLinearGradient(context, gradientRef, startPoint, endPoint, 0);
        CGContextRestoreGState(context);
        
        CGGradientRelease(gradientRef);
        CGColorSpaceRelease(colorSpace);
    }
    
    @end
    

    效果图验证会在后面给出。

    这里重要的是函数CGContextDrawLinearGradient

    /** Gradient and shading functions. **/
    
    /* Fill the current clipping region of `context' with a linear gradient from
       `startPoint' to `endPoint'. The location 0 of `gradient' corresponds to
       `startPoint'; the location 1 of `gradient' corresponds to `endPoint';
       colors are linearly interpolated between these two points based on the
       values of the gradient's locations. The option flags control whether the
       gradient is drawn before the start point or after the end point. */
    
    CG_EXTERN void CGContextDrawLinearGradient(CGContextRef cg_nullable c,
        CGGradientRef cg_nullable gradient, CGPoint startPoint, CGPoint endPoint,
        CGGradientDrawingOptions options)
        CG_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
    

    这个方法的作用是:使用线性渐变填充当前的“上下文”剪切区域startPointendPoint。 “渐变”的位置0对应于 的startPointgradient的位置1对应于endPoint, 颜色在这两点之间基于线性插值渐变位置的值。 选项标志控制是否梯度在起始点或终点之后绘制。

    2. 圆半径外向渐变

    这个可以认为是非线性渐变中的一种。

    #import "JJGradientNonliearVC.h"
    #import "Masonry.h"
    
    @interface JJGradientNonliearVC ()
    
    @end
    
    @implementation JJGradientNonliearVC
    
    #pragma mark - Override Base Function
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        self.title = @"非线性渐变色";
        self.view.backgroundColor = [UIColor lightGrayColor];
        
        //创建CGContextRef
        UIGraphicsBeginImageContext(self.view.bounds.size);
        CGContextRef contextRef = UIGraphicsGetCurrentContext();
        
        //创建CGMutablePathRef
        CGMutablePathRef pathRef = CGPathCreateMutable();
        
        //绘制path
        CGRect rect = CGRectMake(50.0, 100.0, 300.0, 250.0);
        CGPathMoveToPoint(pathRef, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect));
        CGPathAddLineToPoint(pathRef, NULL, CGRectGetMaxX(rect), CGRectGetMinY(rect));
        CGPathAddLineToPoint(pathRef, NULL, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
        CGPathAddLineToPoint(pathRef, NULL, CGRectGetMinX(rect), CGRectGetMaxY(rect));
        CGPathCloseSubpath(pathRef);
        
        //绘制渐变色
        [self drawRadiusGradientWithContext:contextRef path:pathRef beginColor:[UIColor redColor].CGColor endColor:[UIColor greenColor].CGColor];
        
        //释放CGMutablePathRef
        CGPathRelease(pathRef);
        
        //从上下文中获取图像并显示
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
        imageView.contentMode = UIViewContentModeScaleAspectFit;
        [self.view addSubview:imageView];
        
        [imageView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.center.equalTo(self.view);
            make.width.equalTo(@300);
            make.height.equalTo(@250);
        }];
    }
    
    #pragma mark - Object Private Function
    
    - (void)drawRadiusGradientWithContext:(CGContextRef)context
                                     path:(CGPathRef)path
                               beginColor:(CGColorRef)beginColor
                                 endColor:(CGColorRef)endColor;
    {
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGFloat locations[] = {0.0, 1.0};
        NSArray *colorArr = @[(__bridge id)beginColor, (__bridge id)endColor];
        CGGradientRef gradientRef = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colorArr, locations);
        CGRect pathRect = CGPathGetBoundingBox(path);
        
        //具体方向可根据需求修改
        CGPoint centerPoint = CGPointMake(CGRectGetMidX(pathRect), CGRectGetMidY(pathRect));
        CGFloat radius = MAX(pathRect.size.width * 0.5, pathRect.size.height * 0.5) * sqrt(2);
        
        CGContextSaveGState(context);
        CGContextAddPath(context, path);
        CGContextClip(context);
        CGContextDrawRadialGradient(context, gradientRef, centerPoint, 0, centerPoint, radius, 0);
        CGContextRestoreGState(context);
        
        CGGradientRelease(gradientRef);
        CGColorSpaceRelease(colorSpace);
    }
    
    @end
    

    后面会给出这个效果图。

    这里重要的函数是CGContextDrawRadialGradient(context, gradientRef, centerPoint, 0, centerPoint, radius, 0);

    /* Fill the current clipping region of `context' with a radial gradient
       between two circles defined by the center point and radius of each
       circle. The location 0 of `gradient' corresponds to a circle centered at
       `startCenter' with radius `startRadius'; the location 1 of `gradient'
       corresponds to a circle centered at `endCenter' with radius `endRadius';
       colors are linearly interpolated between these two circles based on the
       values of the gradient's locations. The option flags control whether the
       gradient is drawn before the start circle or after the end circle. */
    
    CG_EXTERN void CGContextDrawRadialGradient(CGContextRef cg_nullable c,
        CGGradientRef cg_nullable gradient, CGPoint startCenter, CGFloat startRadius,
        CGPoint endCenter, CGFloat endRadius, CGGradientDrawingOptions options)
        CG_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
    

    它的作用是:用径向渐变填充当前的“上下文”裁剪区域 在由中心点和每个半径定义的两个圆之间圈。 “渐变”的位置0对应于以圆为中心的圆 startCenter,半径为startRadius; “渐变”的位置1对应于以endCenter为中心的圆圈,半径为endRadius; 基于此,这两个圆之间的颜色被线性内插 渐变位置的值。 选项标志控制是否梯度在起始圆之前或结束圆之后绘制。


    功能效果

    先看一下线性渐变的效果。

    线性渐变

    下面看一下圆半径外向渐变色效果。

    圆半径外向渐变

    可见,均可实现效果。

    后记

    未完,待续~~~

    秋意浓

    相关文章

      网友评论

        本文标题:控件渐变色的实现(二)—— Core Graphics实现

        本文链接:https://www.haomeiwen.com/subject/oynalxtx.html