美文网首页
UIBezierPath和CAShapeLayer应用

UIBezierPath和CAShapeLayer应用

作者: O2Space_Xiu | 来源:发表于2016-12-08 00:04 被阅读0次

    CAShapeLayer 是 CALayer 的子类,但是比 CALayer 更灵活,可以画出各种图形。

    别忘记引入QuartzCore.framework

    样例####

    在 CAShapeLayer 中,也可以像 CALayer 一样指定它的 frame 来画,就像这样:

    - (void) Demo01View{
        CAShapeLayer *layer = [CAShapeLayer layer];
        layer.frame = CGRectMake(110, 100, 150, 100);
        layer.backgroundColor = [UIColor redColor].CGColor;
        [self.view.layer addSublayer:layer];
    }
    
    Demo01View

    但是,CAShapeLayer 有一个神奇的属性 path
    用这个属性配合上 UIBezierPath 这个类就可以达到超神的效果。

    UIBezierPath 顾名思义,这是用贝塞尔曲线的方式来构建一段弧线,你可以用任意条弧线来组成你想要的形状,比如,你想用它来和上面一样画一个矩形,那就可以这样子来做:

    - (void) Demo02View{
        //矩形
        UIBezierPath *path01 = [UIBezierPath bezierPathWithRect:CGRectMake(110, 100, 150, 100)];
        CAShapeLayer *layer01 = [CAShapeLayer layer];
        layer01.path = path01.CGPath;
        layer01.fillColor = [UIColor clearColor].CGColor;//填充颜色 不要使用backgroundColor属性
        layer01.strokeColor = [UIColor blackColor].CGColor;//边框颜色
        [self.view.layer addSublayer:layer01];
        
        //圆角矩形
        UIBezierPath *path02 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(110, 235, 150, 100) cornerRadius:50];
        CAShapeLayer *layer02 = [CAShapeLayer layer];
        layer02.path = path02.CGPath;
        layer02.fillColor = [UIColor clearColor].CGColor;
        layer02.strokeColor = [UIColor blackColor].CGColor;
        [self.view.layer addSublayer:layer02];
        
        //圆 顺时针
        CGFloat radius = 60.0;//半径
        CGFloat startAngle = 0.0;//开始角度
        CGFloat endAngle = (M_PI * 2);//结束角度
        UIBezierPath *path03 = [UIBezierPath bezierPathWithArcCenter:CGPointMake([UIScreen mainScreen].bounds.size.width/2.0, 430.0) radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
        CAShapeLayer *layer03 = [CAShapeLayer layer];
        layer03.path = path03.CGPath;
        layer03.fillColor = [UIColor clearColor].CGColor;
        layer03.strokeColor = [UIColor blackColor].CGColor;
        [self.view.layer addSublayer:layer03];
    }
    
    Demo02View

    画曲线####

    贝塞尔曲线的画法是由起点、终点、控制点三个参数来画的,为了解释清楚这个点,我写了几行代码来解释它

    
    - (void) Demo03View{
        CGPoint startPoint = CGPointMake(50, 300);
        CGPoint endPoint = CGPointMake(300, 300);
        CGPoint controlPoint = CGPointMake(170, 200);
        
        CALayer *layer1 = [CALayer layer];
        layer1.frame = CGRectMake(startPoint.x, startPoint.y, 5, 5);
        layer1.backgroundColor = [UIColor redColor].CGColor;
        
        CALayer *layer2 = [CALayer layer];
        layer2.frame = CGRectMake(endPoint.x, endPoint.y, 5, 5);
        layer2.backgroundColor = [UIColor redColor].CGColor;
        
        CALayer *layer3 = [CALayer layer];
        layer3.frame = CGRectMake(controlPoint.x, controlPoint.y, 5, 5);
        layer3.backgroundColor = [UIColor redColor].CGColor;
        
        UIBezierPath *path = [UIBezierPath bezierPath];
        CAShapeLayer *layer = [CAShapeLayer layer];
        
        [path moveToPoint:startPoint];//移动到起始点
        [path addQuadCurveToPoint:endPoint controlPoint:controlPoint];//结束点和控制点
        //CGPoint controlPoint1 = CGPointMake(133, 200);
        //CGPoint controlPoint2 = CGPointMake(216, 400);
        //[path addCurveToPoint:endPoint controlPoint1:controlPoint1 controlPoint2:controlPoint2];//使用两个控制点来画
        
        layer.path = path.CGPath;
        layer.fillColor = [UIColor clearColor].CGColor;
        layer.strokeColor = [UIColor blackColor].CGColor;
        
        [self.view.layer addSublayer:layer];
        [self.view.layer addSublayer:layer1];
        [self.view.layer addSublayer:layer2];
        [self.view.layer addSublayer:layer3];
    }
    
    
    Demo03View

    CAShapeLayer动画###

    动画使用 strokeEnd strokeStart lineWidth三个属性

    // layer是贝塞尔曲线
    // 线从开始点到结束点逐渐显示
    - (void) animation01:(CAShapeLayer *)layer{
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: @"strokeEnd"];
        animation.fromValue = @0;
        animation.toValue = @1;
        animation.duration = 2.0;
        [layer addAnimation:animation forKey:@""];
    }
    // 线从中间向两边逐渐显示
    - (void) animation02:(CAShapeLayer *)layer{
        layer.strokeStart = 0.5;
        layer.strokeEnd = 0.5;
        
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
        animation.fromValue = @0.5;
        animation.toValue = @0;
        animation.duration = 2.0;
        
        CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath: @"strokeEnd"];
        animation2.fromValue = @(0.5);
        animation2.toValue = @1;
        animation2.duration = 2.0;
        
        [layer addAnimation:animation forKey:@""];
        [layer addAnimation:animation2 forKey:@""];
    }
    
    // 线条变粗
    - (void) animation03:(CAShapeLayer *)layer{
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: @"lineWidth"];
        animation.fromValue = @1;
        animation.toValue = @10;
        animation.duration = 2.0;
        [layer addAnimation:animation forKey:@""];
    }
    
    - (void) Demo04View{
        CGSize finalSize = CGSizeMake(CGRectGetWidth(self.view.frame), 400);
        CGFloat layerHeight = finalSize.height * 0.2;
        CAShapeLayer *layer = [CAShapeLayer layer];
        UIBezierPath *bezier = [UIBezierPath bezierPath];
        [bezier moveToPoint:CGPointMake(0, finalSize.height - layerHeight)];
        [bezier addLineToPoint:CGPointMake(0, finalSize.height - 1)];
        [bezier addLineToPoint:CGPointMake(finalSize.width, finalSize.height - 1)];
        [bezier addLineToPoint:CGPointMake(finalSize.width, finalSize.height - layerHeight)];
        [bezier addQuadCurveToPoint:CGPointMake(0,finalSize.height - layerHeight) controlPoint:CGPointMake(finalSize.width / 2, (finalSize.height - layerHeight) - 40)];
        layer.path = bezier.CGPath;
        layer.fillColor = [UIColor blackColor].CGColor;
        [self.view.layer addSublayer:layer];
        
        
        //描点
        CALayer *layer1 = [CALayer layer];
        layer1.frame = CGRectMake(0, finalSize.height - layerHeight, 5, 5);
        layer1.backgroundColor = [UIColor redColor].CGColor;
        [self.view.layer addSublayer:layer1];
        
        CALayer *layer2 = [CALayer layer];
        layer2.frame = CGRectMake(0, finalSize.height - 1, 5, 5);
        layer2.backgroundColor = [UIColor redColor].CGColor;
        [self.view.layer addSublayer:layer2];
        
        CALayer *layer3 = [CALayer layer];
        layer3.frame = CGRectMake(finalSize.width-5, finalSize.height - 1, 5, 5);
        layer3.backgroundColor = [UIColor redColor].CGColor;
        [self.view.layer addSublayer:layer3];
        
        CALayer *layer4 = [CALayer layer];
        layer4.frame = CGRectMake(finalSize.width-5, finalSize.height - layerHeight, 5, 5);
        layer4.backgroundColor = [UIColor redColor].CGColor;
        [self.view.layer addSublayer:layer4];
        
        CALayer *layer5 = [CALayer layer];
        layer5.frame = CGRectMake(finalSize.width / 2, (finalSize.height - layerHeight) - 40, 5, 5);
        layer5.backgroundColor = [UIColor redColor].CGColor;
        [self.view.layer addSublayer:layer5];
    }
    
    Demo04View

    应用###

    微信小眼睛
    .h文件

    #import <UIKit/UIKit.h>
    
    @interface WXPullView : UIView
    
    - (void)animationWith:(CGFloat)y;
    
    @end
    

    .m文件

    #import "WXPullView.h"
    
    @interface WXPullView ()
    
    @property (strong, nonatomic) CAShapeLayer *eyeFirstLightLayer;
    @property (strong, nonatomic) CAShapeLayer *eyeSecondLightLayer;
    @property (strong, nonatomic) CAShapeLayer *eyeballLayer;
    @property (strong, nonatomic) CAShapeLayer *topEyesocketLayer;
    @property (strong, nonatomic) CAShapeLayer *bottomEyesocketLayer;
    
    @end
    
    @implementation WXPullView
    
    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            [self.layer addSublayer:self.eyeFirstLightLayer];
            [self.layer addSublayer:self.eyeSecondLightLayer];
            [self.layer addSublayer:self.eyeballLayer];
            [self.layer addSublayer:self.topEyesocketLayer];
            [self.layer addSublayer:self.bottomEyesocketLayer];
            [self setupAnimation];
        }
        return self;
    }
    
    - (CAShapeLayer *)eyeFirstLightLayer {
        if (!_eyeFirstLightLayer) {
            _eyeFirstLightLayer = [CAShapeLayer layer];
            CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
            UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:CGRectGetWidth(self.frame) * 0.2 startAngle:(230.f / 180.f) * M_PI endAngle:(265.f / 180.f) * M_PI clockwise:YES];
            _eyeFirstLightLayer.borderColor = [UIColor blackColor].CGColor;
            _eyeFirstLightLayer.lineWidth = 5.f;
            _eyeFirstLightLayer.path = path.CGPath;
            _eyeFirstLightLayer.fillColor = [UIColor clearColor].CGColor;
            _eyeFirstLightLayer.strokeColor = [UIColor whiteColor].CGColor;
        }
        return _eyeFirstLightLayer;
    }
    
    - (CAShapeLayer *)eyeSecondLightLayer {
        if (!_eyeSecondLightLayer) {
            _eyeSecondLightLayer = [CAShapeLayer layer];
            CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
            UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:CGRectGetWidth(self.frame) * 0.2 startAngle:(211.f / 180.f) * M_PI endAngle:(220.f / 180.f) * M_PI clockwise:YES];
            _eyeSecondLightLayer.borderColor = [UIColor blackColor].CGColor;
            _eyeSecondLightLayer.lineWidth = 5.f;
            _eyeSecondLightLayer.path = path.CGPath;
            _eyeSecondLightLayer.fillColor = [UIColor clearColor].CGColor;
            _eyeSecondLightLayer.strokeColor = [UIColor whiteColor].CGColor;
        }
        return _eyeSecondLightLayer;
    }
    
    - (CAShapeLayer *)eyeballLayer {
        if (!_eyeballLayer) {
            _eyeballLayer = [CAShapeLayer layer];
            CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
            UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:CGRectGetWidth(self.frame) * 0.3 startAngle:(0.f / 180.f) * M_PI endAngle:(360.f / 180.f) * M_PI clockwise:YES];
            _eyeballLayer.borderColor = [UIColor blackColor].CGColor;
            _eyeballLayer.lineWidth = 1.f;
            _eyeballLayer.path = path.CGPath;
            _eyeballLayer.fillColor = [UIColor clearColor].CGColor;
            _eyeballLayer.strokeColor = [UIColor whiteColor].CGColor;
            _eyeballLayer.anchorPoint = CGPointMake(0.5, 0.5);
        }
        return _eyeballLayer;
    }
    
    - (CAShapeLayer *)topEyesocketLayer {
        if (!_topEyesocketLayer) {
            _topEyesocketLayer = [CAShapeLayer layer];
            CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
            UIBezierPath *path = [UIBezierPath bezierPath];
            [path moveToPoint:CGPointMake(0, CGRectGetHeight(self.frame) / 2)];
            [path addQuadCurveToPoint:CGPointMake(CGRectGetWidth(self.frame), CGRectGetHeight(self.frame) / 2) controlPoint:CGPointMake(CGRectGetWidth(self.frame) / 2, center.y - center.y - 20)];
            _topEyesocketLayer.borderColor = [UIColor blackColor].CGColor;
            _topEyesocketLayer.lineWidth = 1.f;
            _topEyesocketLayer.path = path.CGPath;
            _topEyesocketLayer.fillColor = [UIColor clearColor].CGColor;
            _topEyesocketLayer.strokeColor = [UIColor whiteColor].CGColor;
        }
        return _topEyesocketLayer;
    }
    
    - (CAShapeLayer *)bottomEyesocketLayer {
        if (!_bottomEyesocketLayer) {
            _bottomEyesocketLayer = [CAShapeLayer layer];
            CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
            UIBezierPath *path = [UIBezierPath bezierPath];
            [path moveToPoint:CGPointMake(0, CGRectGetHeight(self.frame) / 2)];
            [path addQuadCurveToPoint:CGPointMake(CGRectGetWidth(self.frame), CGRectGetHeight(self.frame) / 2) controlPoint:CGPointMake(CGRectGetWidth(self.frame) / 2, center.y + center.y + 20)];
            _bottomEyesocketLayer.borderColor = [UIColor blackColor].CGColor;
            _bottomEyesocketLayer.lineWidth = 1.f;
            _bottomEyesocketLayer.path = path.CGPath;
            _bottomEyesocketLayer.fillColor = [UIColor clearColor].CGColor;
            _bottomEyesocketLayer.strokeColor = [UIColor whiteColor].CGColor;
        }
        return _bottomEyesocketLayer;
    }
    
    - (void)setupAnimation {
        self.eyeFirstLightLayer.lineWidth = 0.f;
        self.eyeSecondLightLayer.lineWidth = 0.f;
        self.eyeballLayer.opacity = 0.f;
        _bottomEyesocketLayer.strokeStart = 0.5f;
        _bottomEyesocketLayer.strokeEnd = 0.5f;
        _topEyesocketLayer.strokeStart = 0.5f;
        _topEyesocketLayer.strokeEnd = 0.5f;
    }
    
    - (void)animationWith:(CGFloat)y {
        CGFloat flag = self.frame.origin.y * 2.f - 20.f;
        if (y < flag) {
            if (self.eyeFirstLightLayer.lineWidth < 5.f) {
                self.eyeFirstLightLayer.lineWidth += 1.f;
                self.eyeSecondLightLayer.lineWidth += 1.f;
            }
        }
        if(y < flag - 20) {
            if (self.eyeballLayer.opacity <= 1.0f) {
                self.eyeballLayer.opacity += 0.1f;
            }
        }
        if (y < flag - 40) {
            if (self.topEyesocketLayer.strokeEnd < 1.f && self.topEyesocketLayer.strokeStart > 0.f) {
                self.topEyesocketLayer.strokeEnd += 0.1f;
                self.topEyesocketLayer.strokeStart -= 0.1f;
                self.bottomEyesocketLayer.strokeEnd += 0.1f;
                self.bottomEyesocketLayer.strokeStart -= 0.1f;
            }
        }
        if (y > flag - 40) {
            if (self.topEyesocketLayer.strokeEnd > 0.5f && self.topEyesocketLayer.strokeStart < 0.5f) {
                self.topEyesocketLayer.strokeEnd -= 0.1f;
                self.topEyesocketLayer.strokeStart += 0.1f;
                self.bottomEyesocketLayer.strokeEnd -= 0.1f;
                self.bottomEyesocketLayer.strokeStart += 0.1f;
            }
        }
        if (y > flag - 20) {
            if (self.eyeballLayer.opacity >= 0.0f) {
                self.eyeballLayer.opacity -= 0.1f;
            }
        }
        if (y > flag) {
            if (self.eyeFirstLightLayer.lineWidth > 0.f) {   
                self.eyeFirstLightLayer.lineWidth -= 1.f;
                self.eyeSecondLightLayer.lineWidth -= 1.f;
            }
        }
    }
    @end
    
    微信小眼睛WXPullView.gif

    彩虹进度条
    .h文件

    #import <UIKit/UIKit.h>
    
    @interface RainbowProgress : UIView
    
    //Progress 高度
    @property (nonatomic,assign)CGFloat progressHeigh;
    
    //Progress Value (0~1)
    @property (nonatomic,assign)CGFloat progressValue;
    
    /** 开始动画过程 */
    - (void)startAnimating;
    /** 结束动画过程 */
    - (void)stopAnimating;
    

    .m文件

    #import "RainbowProgress.h"
    
    @interface RainbowProgress ()<CAAnimationDelegate>
    
    /** Animating */
    @property (nonatomic,assign,getter=isAnimating)BOOL animating;
    
    /** CAShapeLayer */
    @property (nonatomic,weak)CAShapeLayer *shapeMaskLayer;
    
    @end
    
    @implementation RainbowProgress
    
    #pragma mark - 将UIView默认的CALayer替换成CAGradientLayer
    +(Class)layerClass{
        return [CAGradientLayer class];
    }
    
    #pragma mark - 初始化方法
    - (instancetype)initWithFrame:(CGRect)frame{
        CGRect originFrame = CGRectMake(0, self.progressHeigh, [UIScreen mainScreen].bounds.size.width, 2);
        self = [super initWithFrame:frame];
        if (self) {
            self.frame = originFrame;
            
            [self setUpRainbowLayer];
        }
        return self;
    }
    -(void)setUpRainbowLayer{
        // 1、创建CAGradientLayer彩虹条颜色层,彩虹颜色当然需要数组存储
        CAGradientLayer* gradientLayer = (CAGradientLayer*)self.layer;
        [gradientLayer setStartPoint:CGPointMake(0, 0)];
        [gradientLayer setEndPoint:CGPointMake(1, 0.01)];
        //gradientLayer.locations = @[@(0.001),@(0.001),@(0.001)];
        NSMutableArray *rainBowColors = [NSMutableArray array];
        for (NSInteger hue = 0; hue <= 360; hue += 5) {
            UIColor *color = [UIColor colorWithHue:1.0*hue/360.0
                                        saturation:1.0
                                        brightness:1.0
                                             alpha:1.0];
            [rainBowColors addObject:(id)color.CGColor];
        }
        gradientLayer.colors = [NSArray arrayWithArray:rainBowColors];
        
        // 2、创建遮罩层 同时也需要贝塞尔曲线
        UIBezierPath* shapePath = [UIBezierPath bezierPath];
        [shapePath moveToPoint:CGPointMake(0, 0)];
        [shapePath addLineToPoint:CGPointMake(self.bounds.size.width, 0)];
        
        CAShapeLayer* shapeMaskLayer = [CAShapeLayer layer];
        
        shapeMaskLayer.path = shapePath.CGPath;
        shapeMaskLayer.lineWidth = 4.f;
        shapeMaskLayer.fillColor = [UIColor clearColor].CGColor;
        shapeMaskLayer.strokeColor = [UIColor blackColor].CGColor;
        // 设置shapeMaskLayer的起止点初始值均为0
        shapeMaskLayer.strokeStart = 0;
        shapeMaskLayer.strokeEnd = 0;
        gradientLayer.mask = shapeMaskLayer;
        
        self.shapeMaskLayer = shapeMaskLayer;
         
    }
    
    #pragma mark - 执行动画的过程
    -(void)performAnimation{
        // Update the colors on the model layer
        
        CAGradientLayer *layer = (id)[self layer];
        NSArray *fromColors = [layer colors];
        NSArray *toColors = [self shiftColors:fromColors];
        [layer setColors:toColors];
        
        // Create an animation to slowly move the hue gradient left to right.
        
        CABasicAnimation *animation;
        animation = [CABasicAnimation animationWithKeyPath:@"colors"];
        [animation setFromValue:fromColors];
        [animation setToValue:toColors];
        [animation setDuration:0.08];                   // CALayer的color切换时间是0.08
        [animation setRemovedOnCompletion:YES];         // 动画完成后是否要移除
        [animation setFillMode:kCAFillModeForwards];
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
        [animation setDelegate:self];
        
        // Add the animation to our layer
        [layer addAnimation:animation forKey:@"animateGradient"];
    }
    /*
     当动画在活动时间内完成或者是从层对象中移除这个代理方法会被调用,如果动画直到它的活动时间末尾没有被移除,那这个'flag'是true的。
     回调和布尔值来保证动画的循环开始并持续以及结束该动画
     */
    - (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag {
        // 当动画在duration时间结束前移除了,这个方法会被调用
        // 然后就下面的if语句判断,如果isAnimation = yes,就重新执行performAnimation
        // 在这个performAnimation中重新创建了核心动画对象,重新设置了代理
        if ([self isAnimating]) {
            [self performAnimation];
        }
    }
    
    - (void)startAnimating {
        if (![self isAnimating]) {
            self.animating = YES;
            [self performAnimation];
        }
    }
    
    - (void)stopAnimating {
        if ([self isAnimating]) {
            self.animating = NO;
        }
    }
    #pragma mark - 辅助方法:转移可变数组最后一个元素到数组的最前面
    - (NSArray *)shiftColors:(NSArray *)colors {
        // Moves the last item in the array to the front
        // shifting all the other elements.
        // 数组中最后一项移动到前面
        // 转移的所有其他元素
        NSMutableArray *mutable = [colors mutableCopy];// 将NSArray数组换成 NSMutableArray
        id last = [mutable lastObject]; // 单独取出最后一个元素
        [mutable removeLastObject];              // 然后将数组中的最后一个元素去除
        [mutable insertObject:last atIndex:0];   // 然后将取出的元素插入到最前面
        return [NSArray arrayWithArray:mutable];
    }
    #pragma mark - 重写set和get方法
    // 重设进度条在父控件的高度位置
    @synthesize progressHeigh = _progressHeigh;
    -(void)setProgressHeigh:(CGFloat)progressHeigh{
        _progressHeigh = progressHeigh;
        // 就要重新设置RainbowProgress这个自定义UIView的高度
        CGRect frame = self.frame;
        frame.origin.y = _progressHeigh;
        self.frame = frame;
    }
    -(CGFloat)progressHeigh{
        if (!_progressHeigh) {
            _progressHeigh = 22;
        }
        return _progressHeigh;
    }
    // 设置进度条的值
    @synthesize progressValue = _progressValue;
    -(void)setProgressValue:(CGFloat)progressValue{
        progressValue = progressValue > 1 ? 1 : progressValue;
        progressValue = progressValue < 0 ? 0 : progressValue;
        _progressValue = progressValue;
        self.shapeMaskLayer.strokeEnd = _progressValue;
    }
    -(CGFloat)progressValue{
        return _progressValue;
    }
    @end
    
    彩虹进度条 RainbowProgress.gif

    相关文章

      网友评论

          本文标题:UIBezierPath和CAShapeLayer应用

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