UIBezierPath详解

作者: 江湖闹士 | 来源:发表于2018-04-12 11:35 被阅读47次

    UIBezierPath中文叫贝塞尔曲线,其作用是 UIBezierPath 类允许你在自定义的 View 中绘制和渲染由直线和曲线组成的路径. 你可以在初始化的时候, 直接为你的 UIBezierPath 指定一个几何图形. 路径可以是简单的几何图形例如: 矩形、椭圆、弧线之类的, 也可以是相对比较复杂的由直线和曲线组成的多边形. 当你定义完图形以后, 你可以使用额外的方法将你的路径直接绘制在当前的绘图上下文中.

    具体的内容和用法下面一一举例

    +(instancetype)bezierPath;
    
    示例:
    UIBezierPath *path0 = [UIBezierPath bezierPath];
    这里只是简单的便利初始化方法,使用它还要设置其他属性进行操作
    
    + (instancetype)bezierPathWithRect:(CGRect)rect;
    
    示例:
    自定义View.m文件中drawRect中写代码
    - (void)drawRect:(CGRect)rect {
      UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.bounds];
      path.lineWidth = 2;
      [[UIColor orangeColor] set];
      [path stroke];
    }
    
    在外面调用自定义View时的代码
        CustonView *vi = [[CustonView alloc] init];
        vi.frame = CGRectMake(100, 100, 100, 100);
        vi.backgroundColor = [UIColor yellowColor];
        [self.view addSubview:vi];
    
    矩形.png
    + (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
    
    示例:
    自定义View.m文件中drawRect中写代码
    - (void)drawRect:(CGRect)rect {
        UIBezierPath *path1 = [UIBezierPath bezierPathWithOvalInRect:self.bounds];
    //    path1.lineWidth = 10;
        [[UIColor orangeColor] set];
        [path1 stroke];
    }
    
    在外面调用自定义View时的代码
        CustonView *vi = [[CustonView alloc] init];
        vi.frame = CGRectMake(100, 100, 100, 50);
        vi.backgroundColor = [UIColor yellowColor];
        [self.view addSubview:vi];
    //如果vi的宽高相等的话椭圆将变成○
    
    椭圆

    但更多情况下我们要的不是显示的状态,而是要切割好的椭圆,怎么做?

    这次不用在自定义View.m drawRect中写代码,可以直接写在用的地方,这里我们用到了CAShapeLayer,是CALayer的子类,CALayer的属性CAShapeLayer都可以用
      示例:
        UIView *vi = [[UIView alloc] initWithFrame:CGRectMake(200, 100, 100, 50)];
        vi.backgroundColor = [UIColor yellowColor];
        [self.view addSubview:vi];
    
    //*****************关键代码开始*********************
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithOvalInRect:vi1.bounds];
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.frame = vi.bounds;
        maskLayer.path = maskPath.CGPath;
        vi.layer.mask = maskLayer;
    //*****************关键代码结束*****************
    
    矩形View切割后的椭圆.png
    + (instancetype) bezierPathWithRoundedRect:(CGRect)rect 
                                  cornerRadius:(CGFloat)cornerRadius;
    
      示例:
    //****************在drawRect中写(显示路径)*************
    - (void)drawRect:(CGRect)rect {
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:25];
        [path stroke];
    }
    
    //controller中调用
        CustonView *vi = [[CustonView alloc] init];
        vi.frame = CGRectMake(100, 100, 100, 50);
        vi.backgroundColor = [UIColor yellowColor];
        [self.view addSubview:vi];
    
    //****************直接用(切割)*************
        UIView *vi = [[UIView alloc] initWithFrame:CGRectMake(200, 100, 100, 50)];
        vi.backgroundColor = [UIColor yellowColor];
        [self.view addSubview:vi];
    
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:vi.bounds cornerRadius:25];
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.frame = vi.bounds;
        maskLayer.path = maskPath.CGPath;
        vi.layer.mask = maskLayer;
    
    显示路径.png
    切割.png
    + (instancetype) bezierPathWithRoundedRect:(CGRect)rect 
                             byRoundingCorners:(UIRectCorner)corners
                                   cornerRadii:(CGSize)cornerRadii;
    
      示例:
    //****************在drawRect中写(显示路径)*************
    - (void)drawRect:(CGRect)rect {
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(25, 25)];
        [path stroke];
    }
    
    //controller中调用
        CustonView *vi = [[CustonView alloc] init];
        vi.frame = CGRectMake(100, 100, 100, 50);
        vi.backgroundColor = [UIColor yellowColor];
        [self.view addSubview:vi];
    
    //****************直接用(切割)*************
        UIView *vi = [[UIView alloc] initWithFrame:CGRectMake(200, 100, 100, 50)];
        vi.backgroundColor = [UIColor yellowColor];
        [self.view addSubview:vi];
    
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:vi.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(25, 25)];
        CAShapeLayer *maskLayer = [CAShapeLayer layer];
        maskLayer.frame = vi.bounds;
        maskLayer.path = maskPath.CGPath;
        vi.layer.mask = maskLayer;
    
    
    这里用到了一个枚举
    typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
        UIRectCornerTopLeft     = 1 << 0,
        UIRectCornerTopRight    = 1 << 1,
        UIRectCornerBottomLeft  = 1 << 2,
        UIRectCornerBottomRight = 1 << 3,
        UIRectCornerAllCorners  = ~0UL
    };
    是哪个角需要设圆角,用 | 符号连接即可
    
    显示路径.png 切割.png
    + (instancetype) bezierPathWithArcCenter:(CGPoint)center     //圆心
                                      radius:(CGFloat)radius     //半径
                                  startAngle:(CGFloat)startAngle //开始角度
                                    endAngle:(CGFloat)endAngle   //结束角度
                                   clockwise:(BOOL)clockwise;    //顺时针or逆时针
    
    示例.jpg
    + (instancetype) bezierPathWithCGPath:(CGPathRef)CGPath;
    
    CGPath是UIBezierPath的一个只读属性,通常获取一个UIBezierPath对象的path可以给其他地方用比如上面例子中的 maskLayer.path = maskPath.CGPath;
    
    - (UIBezierPath *) bezierPathByReversingPath;
    
    这里的反方向指的是初始位置和末位置调换,而不是图形反向,比如一条路径是从(0,0)到(3,4),(0,0)是初始位置,(3,4)是末位置,如果使用此方法,则新的UIBezierPath对象的初始位置为(3,4),末位置是(0,0),但是两条直线看上去是一模一样的
    

    除了一些常用的便利方法,还有一个对象方法用于自定义一些特殊的路径

    - (void)moveToPoint:(CGPoint)point;
    
    - (void)addLineToPoint:(CGPoint)point;
    
    - (void)addArcWithCenter:(CGPoint)center 
                      radius:(CGFloat)radius 
                  startAngle:(CGFloat)startAngle 
                    endAngle:(CGFloat)endAngle 
                   clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
    
    - (void)addQuadCurveToPoint:(CGPoint)endPoint 
                   controlPoint:(CGPoint)controlPoint;
    
    二次贝塞尔曲线.jpg
    - (void)addCurveToPoint:(CGPoint)endPoint 
              controlPoint1:(CGPoint)controlPoint1 
              controlPoint2:(CGPoint)controlPoint2;
    
    三次贝塞尔曲线.jpg
    //意思就是闭合路径,初始位置和末位置点连成直线,形成闭合的状态
    - (void)closePath;
    
    - (void)removeAllPoints;
    
    //该方法将会在当前 UIBezierPath 对象的路径中追加
    - (void)appendPath:(UIBezierPath *)bezierPath;
    

    UIBezierPath的属性

    /**
      * 获取这个属性, 你将会获得一个不可变的 CGPathRef 对象,
      * 他可以传入 CoreGraphics 提供的函数中
      * 你可以是用 CoreGraphics 框架提供的方法创建一个路径, 
      * 并给这个属性赋值, 当时设置了一个新的路径后, 
      * 这个将会对你给出的路径对象进行 Copy 操作
      */
    @property(nonatomic) CGPathRef CGPath;
    //上文提过
    
    /**
      * 该属性的值, 将会是下一条绘制的直线或曲线的起始点.
      * 如果当前路径为空, 那么该属性的值将会是 CGPointZero
      */
    @property(nonatomic, readonly) CGPoint currentPoint;
    
    /**
      * 线宽属性定义了 `UIBezierPath` 对象中绘制的曲线规格. 默认为: 1.0
      */
    @property(nonatomic) CGFloat lineWidth;
    
    /**
      * 该属性应用于曲线的终点和起点. 该属性在一个闭合子路经中是无效果的. 默认为: kCGLineCapButt
      */
    @property(nonatomic) CGLineCap lineCapStyle;
    
    
    // CGPath.h
    /* Line cap styles. */
    typedef CF_ENUM(int32_t, CGLineCap) {
        kCGLineCapButt,
        kCGLineCapRound,
        kCGLineCapSquare
    };
    
    曲线终点样式.png
    /**
      * 默认为: kCGLineJoinMiter.
      */
    @property(nonatomic) CGLineJoin lineJoinStyle;
    
    
    // CGPath.h
    /* Line join styles. */
    typedef CF_ENUM(int32_t, CGLineJoin) {
        kCGLineJoinMiter,
        kCGLineJoinRound,
        kCGLineJoinBevel
    };
    
    曲线连接点样式.png
    /**
      * 两条线交汇处内角和外角之间的最大距离, 只有当连接点样式为 kCGLineJoinMiter
      * 时才会生效,最大限制为10
      * 我们都知道, 两条直线相交时, 夹角越小, 斜接长度就越大.
      * 该属性就是用来控制最大斜接长度的.
      * 当我们设置了该属性, 如果斜接长度超过我们设置的范围, 
      * 则连接处将会以 kCGLineJoinBevel 连接类型进行显示.
      */
    @property(nonatomic) CGFloat miterLimit;
    
    内角和外角距离.png
    /**
      * 该属性用来确定渲染曲线路径的精确度.
      * 该属性的值用来测量真实曲线的点和渲染曲线的点的最大允许距离.
      * 值越小, 渲染精度越高, 会产生相对更平滑的曲线, 但是需要花费更
      * 多的计算时间. 值越大导致则会降低渲染精度, 这会使得渲染的更迅
      * 速. flatness 的默认值为 0.6.
      * Note: 大多数情况下, 我们都不需要修改这个属性的值. 然而当我们
      *       希望以最小的消耗去绘制一个临时的曲线时, 我们也许会临时增
      *       大这个值, 来获得更快的渲染速度.
      */
    
    @property(nonatomic) CGFloat flatness;
    
    /**
      * 设置为 YES, 则路径将会使用 基偶规则 (even-odd) 进行填充.
      * 设置为 NO,  则路径将会使用 非零规则 (non-zero) 规则进行填充.
      */
    @property(nonatomic) BOOL usesEvenOddFillRule;
    
    /**
      * @param pattern: 该属性是一个 C 语言的数组, 其中每一个元素都是 CGFloat
      *                 数组中的元素代表着线段每一部分的长度, 第一个元素代表线段的第一条线,
      *                 第二个元素代表线段中的第一个间隙. 这个数组中的值是轮流的. 来解释一下
      *                 什么叫轮流的. 
      *                 举个例子: 声明一个数组 CGFloat dash[] = @{3.0, 1.0}; 
      *                 这意味着绘制的虚线的第一部分长度为3.0, 第一个间隙长度为1.0, 虚线的
      *                 第二部分长度为3.0, 第二个间隙长度为1.0. 以此类推.
    
      * @param count: 这个参数是 pattern 数组的个数
      * @param phase: 这个参数代表着, 虚线从哪里开始绘制.
      *                 举个例子: 这是 phase 为 7. pattern[] = @{8.0,3.0,16.0,7.0}; 那么虚线将会从第7个像素开始,由于第一个实线的长度是8像素,所以会先显示1像素实线,然后3像素空白,16像素实线,7像素空白,8像素实线,3像素空白……
      */
    - (void)setLineDash:(const CGFloat *)pattern
                  count:(NSInteger)count
                  phase:(CGFloat)phase;
    
    示例:
     示例:
    //****************在drawRect中写(显示路径)*************
    - (void)drawRect:(CGRect)rect {
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointMake(0, 10)];
        [path addLineToPoint:CGPointMake(100, 80)];
        path.lineWidth = 2;
        [[UIColor orangeColor] set];
        CGFloat dash[] = {8.0,3.0,16.0,7.0};
        [path setLineDash:dash count:4 phase:7];
        [path stroke];
    }
    
    //controller中调用
        CustonView *vi = [[CustonView alloc] init];
        vi.frame = CGRectMake(100, 100, 100, 100);
        [self.view addSubview:vi];
    
    下图中黑色为vi,虚线的两端位置是相对于vi的位置定的(0,10)(100,80)
    
    虚线.png
    /**
      * 该方法可以重新获取之前设置过的虚线样式.
      *  Note:  pattern 这个参数的容量必须大于该方法返回数组的容量.
      *         如果无法确定数组的容量, 那么可以调用两次该方法, 第一次
      *         调用该方法的时候, 传入 count 参数, 然后在用 count 参数
      *         来申请 pattern 数组的内存空间. 然后再第二次正常的调用该方法
      */
    - (void)getLineDash:(CGFloat *)pattern 
                  count:(NSInteger *)count
                  phase:(CGFloat *)phase;
    

    绘制路径

    /**
      * 该方法当前的填充颜色 和 绘图属性对路径的封闭区域进行填充.
      * 如果当前路径是一条开放路径, 该方法将会隐式的将路径进行关闭后进行填充
      * 该方法在进行填充操作之前, 会自动保存当前绘图的状态, 所以我们不需要
      * 自己手动的去保存绘图状态了. 
      */
    - (void)fill;
    
    /**
      * 该方法当前的填充颜色 和 绘图属性 (外加指定的混合模式 和 透明度) 
      * 对路径的封闭区域进行填充. 如果当前路径是一条开放路径, 该方法将
      * 会隐式的将路径进行关闭后进行填充
      * 该方法在进行填充操作之前, 会自动保存当前绘图的状态, 所以我们不需要
      * 自己手动的去保存绘图状态了. 
      *
      * @param blendMode: 混合模式决定了如何和已经存在的被渲染过的内容进行合成
      * @param alpha: 填充路径时的透明度
      */
    - (void)fillWithBlendMode:(CGBlendMode)blendMode 
                        alpha:(CGFloat)alpha;
    
    - (void)stroke;
    
    /**
      * @param blendMode: 混合模式决定了如何和已经存在的被渲染过的内容进行合成
      * @param alpha: 填充路径时的透明度
      */
    - (void)strokeWithBlendMode:(CGBlendMode)blendMode
                          alpha:(CGFloat)alpha;
    
    /**
      *  该方法返回一个布尔值, 当曲线的覆盖区域包含
      * 指定的点(内部点), 则返回 YES, 否则返回 NO. 
      * Note: 如果当前的路径是一个开放的路径, 那么
      *       就算指定点在路径覆盖范围内, 该方法仍然会
      *       返回 NO, 所以如果你想判断一个点是否在一个
      *       开放路径的范围内时, 你需要先Copy一份路径,
      *       并调用 -(void)closePath; 将路径封闭, 然后
      *       再调用此方法来判断指定点是否是内部点.
      * @param point: 指定点.
      */
    - (BOOL) containsPoint:(CGPoint)point;
    
    /**
      * 检测当前路径是否绘制过直线或曲线.
      * Note: 记住, 就算你仅仅调用了 moveToPoint 方法
      *       那么当前路径也被看做不为空.
      */
    @property (readonly, getter=isEmpty) BOOL empty;
    
    /**
      * 该属性描述的是一个能够完全包含路径中所有点
      *  的一个最小的矩形区域. 该区域包含二次贝塞尔
      *  曲线和三次贝塞尔曲线的控制点.
      */
    @property (nonatomic, readonly) CGRect bounds;
    

    该方法将会直接对路径中的所有点进行指定的放射

    /**
      * 该方法将会直接对路径中的所有点进行指定的放射
      * 变换操作. 
      */
    - (void)applyTransform:(CGAffineTransform)transform;
    
    示例:
    - (void) drawRect:(CGRect)rect {
      
      UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint: CGPointMake(50, 50)];
        [path addArcWithCenter:CGPointMake(50, 50) radius:30 startAngle:0 endAngle:M_PI clockwise:YES];
        path.lineWidth = 3;
        
        UIBezierPath *reversingPath = [path bezierPathByReversingPath];
        reversingPath.lineWidth = 3;
        
        CGAffineTransform transform = CGAffineTransformMakeTranslation(100, 0);
        [path applyTransform: transform];
        
        [[UIColor redColor] set];
        [path stroke];
        
        [[UIColor greenColor] set];
        [reversingPath stroke];
    }
    
    //controller中调用
        custonView *vi = [[custonView alloc] init];
        vi.frame = CGRectMake(100, 100, 300, 300);
        [self.view addSubview:vi];
    
    平移.png

    相关文章

      网友评论

        本文标题:UIBezierPath详解

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