iOS 简单的贝塞尔(UIBezierPath)曲线使用

在iOS中绘制矢量图或者路径的时候通常会用到 UIBezierPath ,它在 UIKit 中,是CoreGraphics对path的封装。使用 UIBezierPath ,可以绘制直线、椭圆、多边形和贝塞尔曲线等。

使用UIBezierPath的方法

UIBezierPath 是对 CGPathRef 的封装。创建矢量图形时,拆解成一或多条线段,拼接起来,每条线段的终点都是下一条线段的起点。

具体地:

  1. 创建一个 UIBezierPath 对象
  2. 用 moveToPoint: 设置初始线段的起点
  3. 添加线段,定义一或多个子路径
  4. 修改 UIBezierPath 的绘图相关的属性,比如stroke path的属性 lineWidth 和 lineJoinStyle , filled path的属性 usesEvenOddFillRule

如果是矩形或者椭圆之类的特殊图形,可以不用第2步。

(简单的说 需要一个起点然后添加连线(addLineToPoint-直线,addQuadCurveToPoint-椭圆)或者其他的 )

绘制各类矢量图形

UIBezierPath 包含了几个特殊形状的类方法,很容易使用。

// 创建矩形
+ (UIBezierPath *)bezierPathWithRect:(CGRect)rect

// 创建圆角矩形
+ (UIBezierPath *)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius

// 创建矩形内切圆
+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect

// 创建弧形
+ (UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise

除了这些闭合的特殊路径,也有一些方法用来添加子路径。

// 添加直线
- (void)addLineToPoint:(CGPoint)point

// 添加弧形线段
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise

// 添加二阶贝塞尔曲线
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint

// 添加三阶贝塞尔曲线
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2

UIBezierPath 封装了 CGPathRef ,它提供了 CGPath 属性使我们可以获取底层的path。

如果我们还希望修改封装的path,有两种方法。一种是完全用CoreGraphics的函数修改,另一种是混用CoreGraphics和 UIBezierPath 的方法。

UIBezierPath主要是画出形状(或画出一个图形的路径path),他可以配合其他的layer使用(CAShapeLayer,CAGradientLayer等)layer可以添加动画 所以结合起来更强大

例如:创建一个简单的弧形

-(UIBezierPath *)confighPathY:(CGFloat)y X:(CGFloat)x

{

    CGFloat width =self.frame.size.width;

    [self.movePathremoveAllPoints];

    [self.movePathmoveToPoint:CGPointMake(0,0)];

    [self.movePathaddLineToPoint:CGPointMake(width,0)];

    [self.movePathaddQuadCurveToPoint:CGPointMake(0,0)controlPoint:CGPointMake(width /2.0 + x<width/2.0?(-x/2.0):(x), y)];

    return self.movePath;

}

-(UIBezierPath *)movePath

{

    if (_movePath ==nil)

    {

        _movePath = [UIBezierPathbezierPath];

    }

    return_movePath;

}

创建好路径之后 结合 Layer

// 创建一个shapeLayer

CAShapeLayer *layer = [CAShapeLayerlayer];

layer.frame         = showView.bounds;                // showViewframe一致

layer.strokeColor   = [UIColor greenColor].CGColor;   // 边缘线的颜色

layer.fillColor     = [UIColor clearColor].CGColor;   // 闭环填充的颜色

layer.lineCap       = kCALineCapSquare;               // 边缘线的类型

layer.path          = [selfconfighPathY:200 X:100].CGPath;                    // 从贝塞尔曲线获取到形状

layer.lineWidth     = 4.0f;                          //线条宽度

layer.strokeStart   = 0.0f;

layer.strokeEnd     = 0.1f; 

// 将layer添加进图层

 [showView.layer addSublayer:layer];


整合一些简单的动画 (路径self.movePath 到 self.originPath的动画

-(void)animation

{

    CABasicAnimation *morph = [CABasicAnimationanimationWithKeyPath:@"path"];

    morph.duration = 0.5;

    morph.fromValue = (__bridgeid_Nullable)(self.movePath.CGPath);

    morph.toValue = (__bridgeid_Nullable)(self.originPath.CGPath);

   //移动后位置保持结束后的状态

    morph.fillMode=kCAFillModeForwards;

    morph.removedOnCompletion =NO;

    

    [self.shapeLayeraddAnimation:morphforKey:nil];

}

(以上的代码不是连贯的  只是说明一下场景)



===以上知识大致讲解完毕接来下就是上代码===================

我们自定义一个view,根据手势滑动 让曲线跟着移动 

GIF效果图:


  




一下是源代码:

.h

//
//  CADisplayView.h
//  SearchVCDemo
//
//  Created by Programmer two on 16/1/16.
//  Copyright © 2016年 linpeng. All rights reserved.
//

#import <UIKit/UIKit.h>

// 16进制转颜色
#define UIColorFromRGB(rgbValue)            [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
#define kMAINGREEN  UIColorFromRGB(0x3CDC46)


@interface CADisplayView : UIView
@property (nonatomic,strong) CADisplayLink *displayLink;
@property (nonatomic,) CFTimeInterval beginTime;
@end

.m

//
//  CADisplayView.m
//  SearchVCDemo
//
//  Created by Programmer two on 16/1/16.
//  Copyright © 2016年 linpeng. All rights reserved.
//

#import "CADisplayView.h"

@interface CADisplayView()
@property (nonatomic,strong) CAShapeLayer *shapeLayer;
@property (nonatomic,strong) UIBezierPath *movePath,*originPath;
@end
@implementation CADisplayView
CGFloat x;
CGFloat y;

-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CGPoint point = [touches.anyObject locationInView:self];
    x = point.x;
    y = point.y;
    [self setNeedsDisplayView];
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    y = 0;
    [self animation];
}

-(UIBezierPath *)confighPathY:(CGFloat)y X:(CGFloat)x
{
    CGFloat width = self.frame.size.width;
    [self.movePath removeAllPoints];
    [self.movePath moveToPoint:CGPointMake(0,0)];
    [self.movePath addLineToPoint:CGPointMake(width, 0)];
    [self.movePath addQuadCurveToPoint:CGPointMake(0, 0) controlPoint:CGPointMake(width /2.0 + x<width/2.0?(-x/2.0):(x), y)];
    return self.movePath;
}
-(void)animation
{
    CABasicAnimation *morph = [CABasicAnimation animationWithKeyPath:@"path"];
    morph.duration = 0.5;
    morph.fromValue = (__bridge id _Nullable)(self.movePath.CGPath);
    morph.toValue = (__bridge id _Nullable)(self.originPath.CGPath);
   //移动后位置保持结束后的状态
    morph.fillMode=kCAFillModeForwards;
    morph.removedOnCompletion = NO;
    
    [self.shapeLayer addAnimation:morph forKey:nil];
}
//刷新path
-(void)setNeedsDisplayView
{
    //除移之前的
    [self.shapeLayer removeFromSuperlayer];
    
    self.shapeLayer = [CAShapeLayer layer];
    self.shapeLayer.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);//设置shapeLayer的尺寸和位置
    self.shapeLayer.fillColor = [kMAINGREEN colorWithAlphaComponent:0.5].CGColor;//填充颜色为ClearColor
    //设置线条的宽度和颜色
    self.shapeLayer.lineWidth = 1.0f;
    self.shapeLayer.strokeColor = [UIColor clearColor].CGColor;
    //让贝塞尔曲线与CAShapeLayer产生联系
    self.shapeLayer.path = [self confighPathY:y X:x].CGPath;
    //添加并显示
    [self.layer addSublayer:self.shapeLayer];
}

/* 调试发现在drawRect里面处理很耗内存
-(void)drawRect:(CGRect)rect
{
    //除移之前的
    [self.shapeLayer removeFromSuperlayer];
    
    self.shapeLayer = [CAShapeLayer layer];
    self.shapeLayer.frame = CGRectMake(0, 0, rect.size.width, rect.size.height);//设置shapeLayer的尺寸和位置
    self.shapeLayer.fillColor = [kMAINGREEN colorWithAlphaComponent:0.5].CGColor;//填充颜色为ClearColor
    //设置线条的宽度和颜色
    self.shapeLayer.lineWidth = 1.0f;
    self.shapeLayer.strokeColor = [UIColor clearColor].CGColor;
    //让贝塞尔曲线与CAShapeLayer产生联系
    self.shapeLayer.path = [self confighPathY:y X:x].CGPath;
    //添加并显示
    [self.layer addSublayer:self.shapeLayer];
}
 */

-(UIBezierPath *)movePath
{
    if (_movePath == nil)
    {
        _movePath = [UIBezierPath bezierPath];
    }
    return _movePath;
}
-(UIBezierPath *)originPath
{
    if (_originPath == nil)
    {
        CGFloat width = self.frame.size.width;
        _originPath = [UIBezierPath bezierPath];
        [_originPath moveToPoint:CGPointMake(0,0)];
        [_originPath addLineToPoint:CGPointMake(width, 0)];
        [_originPath addQuadCurveToPoint:CGPointMake(0, 0) controlPoint:CGPointMake(width /2.0 + x<width/2.0?(-x/2.0):(x), 0)];
    }
    return _originPath;
}

@end


VC调用:

 int w = self.view.frame.size.width;
    CADisplayView *v1 = [[CADisplayView alloc] initWithFrame:CGRectMake((self.view.frame.size.width - w)/2.0, 200, w, w)];
    [v1 setBackgroundColor:[UIColor whiteColor]];
    [self.view addSubview:v1];


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值