iOS - Quartz 2D 贝塞尔曲线

简介: 1、贝塞尔曲线贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。

1、贝塞尔曲线

  • 贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如 PhotoShop 等。在 Flash4 中还没有完整的曲线工具,而在 Flash5 里面已经提供出贝塞尔曲线工具。

  • 二阶贝塞尔曲线示意图

    Quartz2D2

  • 三阶贝塞尔曲线示意图

    Quartz2D3

  • 贝塞尔路径(UIBezierPath)是 iOS UIKit 框架中对 Quartz 2D 绘图的封装。实际操作起来,使用贝塞尔路径,更为方便。用法与 CGContextRef 类似,但是 OC 对其进行了封装,更加面向对象。

  • 贝塞尔路径常用的方法

        // 设置起始点
        - (void)moveToPoint:(CGPoint)point;
    
        // 添加直线到一点
        - (void)addLineToPoint:(CGPoint)point;
    
        // 封闭闭路径
        - (void)closePath;
    
        // 返回一个描述椭圆的路径
        + (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect;
    
        // 贝塞尔曲线
        - (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
    
        // 三次贝塞尔曲线
        - (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
    
        // 绘制圆弧
        - (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

2、基本图形的绘制


3、二三阶贝塞尔曲线示例

3.1 二阶贝塞尔曲线示例

  • QBezierPathView.h

        @interface QBezierPathView : UIView
    
        @end
  • QBezierPathView.m

        @interface QBezierPathView ()
    
        /// 路径
        @property (nonatomic, strong) UIBezierPath *path;
    
        /// 起始点
        @property (nonatomic, assign) CGPoint startP;
    
        /// 终止点
        @property (nonatomic, assign) CGPoint endP;
    
        /// 控制点
        @property (nonatomic, assign) CGPoint controlP;
    
        /// 线的颜色
        @property (nonatomic, strong) UIColor *pathColor;
    
        /// 线的宽度
        @property (nonatomic, assign) CGFloat pathWidth;
    
        /// 当前触摸的点
        @property (nonatomic, assign) NSUInteger currentTouchP;
    
        @end
    
        @implementation QBezierPathView
    
        /// 初始化
        - (instancetype)initWithFrame:(CGRect)frame {
    
            if (self = [super initWithFrame:frame]) {
    
                // 设置初始值
    
                self.startP    = CGPointMake(20, 300);
                self.endP      = CGPointMake(250, 300);
                self.controlP  = CGPointMake(100, 100);
    
                self.pathColor = [UIColor redColor];
                self.pathWidth = 2;
            }
            return self;
        }
    
        /// 绘制二阶贝塞尔曲线
        - (void)drawRect:(CGRect)rect {
    
            // 绘制贝塞尔曲线
    
            self.path = [UIBezierPath bezierPath];
            [self.path moveToPoint:self.startP];
    
            [self.path addQuadCurveToPoint:self.endP controlPoint:self.controlP];
    
            self.path.lineWidth = self.pathWidth;
            [self.pathColor setStroke];
            [self.path stroke];
    
            // 绘制辅助线
    
            self.path = [UIBezierPath bezierPath];
    
            self.path.lineWidth = 1;
            [[UIColor grayColor] setStroke];
    
            CGFloat lengths[] = {5};
            [self.path setLineDash:lengths count:1 phase:1];
    
            [self.path moveToPoint:self.controlP];
            [self.path addLineToPoint:self.startP];
            [self.path stroke];
    
            [self.path moveToPoint:self.controlP];
            [self.path addLineToPoint:self.endP];
            [self.path stroke];
    
            // 绘制辅助点及信息
    
            self.path = [UIBezierPath bezierPathWithArcCenter:self.startP radius:4 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
            [[UIColor blackColor] setStroke];
            [self.path fill];
    
            self.path = [UIBezierPath bezierPathWithArcCenter:self.endP radius:4 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
            [[UIColor blackColor] setStroke];
            [self.path fill];
    
            self.path = [UIBezierPath bezierPathWithArcCenter:self.controlP radius:3 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
            self.path.lineWidth = 2;
            [[UIColor blackColor] setStroke];
            [self.path stroke];
    
            CGRect startMsgRect = CGRectMake(self.startP.x + 8, self.startP.y - 7, 50, 20);
            [@"起始点" drawInRect:startMsgRect withAttributes:nil];
    
            CGRect endMsgRect = CGRectMake(self.endP.x + 8, self.endP.y - 7, 50, 20);
            [@"终止点" drawInRect:endMsgRect withAttributes:nil];
    
            CGRect control1MsgRect = CGRectMake(self.controlP.x + 8, self.controlP.y - 7, 50, 20);
            [@"控制点" drawInRect:control1MsgRect withAttributes:nil];
        }
    
        /// 触摸开始
        - (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event {
    
            // 获取触摸点位置
            CGPoint startPoint = [touches.anyObject locationInView:self];
    
            CGRect startR   = CGRectMake(self.startP.x   - 4, self.startP.y   - 4, 8, 8);
            CGRect endR     = CGRectMake(self.endP.x     - 4, self.endP.y     - 4, 8, 8);
            CGRect controlR = CGRectMake(self.controlP.x - 4, self.controlP.y - 4, 8, 8);
    
            // 判断当前触摸点
            if (CGRectContainsPoint(startR, startPoint)) {
                self.currentTouchP = 1;
            } else if (CGRectContainsPoint(endR, startPoint)) {
                self.currentTouchP = 2;
            } else if (CGRectContainsPoint(controlR, startPoint)) {
                self.currentTouchP = 3;
            }
        }
    
        /// 触摸移动
        - (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event {
    
            // 获取触摸点位置
            CGPoint touchPoint = [touches.anyObject locationInView:self];
    
            // 限制触摸点的边界
    
            if (touchPoint.x < 0) {
                touchPoint.x = 0;
            }
    
            if (touchPoint.x > self.bounds.size.width) {
                touchPoint.x = self.bounds.size.width;
            }
    
            if (touchPoint.y < 0) {
                touchPoint.y = 0;
            }
    
            if (touchPoint.y > self.bounds.size.height) {
                touchPoint.y = self.bounds.size.height;
            }
    
            // 设置当前触摸点的值
            switch (self.currentTouchP) {
                case 1:
                    self.startP = touchPoint;
                    break;
    
                case 2:
                    self.endP = touchPoint;
                    break;
    
                case 3:
                    self.controlP = touchPoint;
                    break;
    
                default:
                    break;
            }
    
            // 刷新
            [self setNeedsDisplay];
        }
    
        /// 触摸结束
        - (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event {
    
            // 释放之前的触摸点
            self.currentTouchP = 0;
        }
    
        /// 触摸取消
        - (void)touchesCancelled:(NSSet *)touches withEvent:(nullable UIEvent *)event {
            [self touchesEnded:touches withEvent:event];
        }
    
        @end
  • ViewController.m

        CGRect frame = CGRectMake(20, 50, self.view.bounds.size.width - 40, 400);
    
        QBezierPathView *pathView = [[QBezierPathView alloc] initWithFrame:frame];
    
        pathView.backgroundColor = [UIColor whiteColor];
        pathView.layer.borderWidth = 1;
    
        [self.view addSubview:pathView];
  • 效果

    Quartz2D112Quartz2D113

3.2 三阶贝塞尔曲线示例

  • QBezierPathView.h

        @interface QBezierPathView : UIView
    
        @end
  • QBezierPathView.m

        @interface QBezierPathView : UIView
        @interface QBezierPathView ()
    
        /// 路径
        @property (nonatomic, strong) UIBezierPath *path;
    
        /// 起始点
        @property (nonatomic, assign) CGPoint startP;
    
        /// 终止点
        @property (nonatomic, assign) CGPoint endP;
    
        /// 控制点
        @property (nonatomic, assign) CGPoint controlP1;
        @property (nonatomic, assign) CGPoint controlP2;
    
        /// 线的颜色
        @property (nonatomic, strong) UIColor *pathColor;
    
        /// 线的宽度
        @property (nonatomic, assign) CGFloat pathWidth;
    
        /// 当前触摸的点
        @property (nonatomic, assign) NSUInteger currentTouchP;
    
        @end
    
        @implementation QBezierPathView
    
        /// 初始化
        - (instancetype)initWithFrame:(CGRect)frame {
    
            if (self = [super initWithFrame:frame]) {
    
                // 设置初始值
    
                self.startP    = CGPointMake(20, 300);
                self.endP      = CGPointMake(250, 300);
                self.controlP1  = CGPointMake(100, 100);
                self.controlP2  = CGPointMake(200, 350);
    
                self.pathColor = [UIColor redColor];
                self.pathWidth = 2;
            }
            return self;
        }
    
        /// 绘制二阶贝塞尔曲线
        - (void)drawRect:(CGRect)rect {
    
            // 绘制贝塞尔曲线
    
            self.path = [UIBezierPath bezierPath];
            [self.path moveToPoint:self.startP];
    
            [self.path addCurveToPoint:self.endP controlPoint1:self.controlP1 controlPoint2:self.controlP2];
    
            self.path.lineWidth = self.pathWidth;
            [self.pathColor setStroke];
            [self.path stroke];
    
            // 绘制辅助线
    
            self.path = [UIBezierPath bezierPath];
    
            self.path.lineWidth = 1;
            [[UIColor grayColor] setStroke];
    
            CGFloat lengths[] = {5};
            [self.path setLineDash:lengths count:1 phase:1];
    
            [self.path moveToPoint:self.controlP1];
            [self.path addLineToPoint:self.startP];
            [self.path stroke];
    
            [self.path moveToPoint:self.controlP1];
            [self.path addLineToPoint:self.controlP2];
            [self.path stroke];
    
            [self.path moveToPoint:self.controlP2];
            [self.path addLineToPoint:self.endP];
            [self.path stroke];
    
            // 绘制辅助点及信息
    
            self.path = [UIBezierPath bezierPathWithArcCenter:self.startP radius:4 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
            [[UIColor blackColor] setStroke];
            [self.path fill];
    
            self.path = [UIBezierPath bezierPathWithArcCenter:self.endP radius:4 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
            [[UIColor blackColor] setStroke];
            [self.path fill];
    
            self.path = [UIBezierPath bezierPathWithArcCenter:self.controlP1 radius:3 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
            self.path.lineWidth = 2;
            [[UIColor blackColor] setStroke];
            [self.path stroke];
    
            self.path = [UIBezierPath bezierPathWithArcCenter:self.controlP2 radius:3 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
            self.path.lineWidth = 2;
            [[UIColor blackColor] setStroke];
            [self.path stroke];
    
            CGRect startMsgRect = CGRectMake(self.startP.x + 8, self.startP.y - 7, 50, 20);
            [@"起始点" drawInRect:startMsgRect withAttributes:nil];
    
            CGRect endMsgRect = CGRectMake(self.endP.x + 8, self.endP.y - 7, 50, 20);
            [@"终止点" drawInRect:endMsgRect withAttributes:nil];
    
            CGRect control1MsgRect = CGRectMake(self.controlP1.x + 8, self.controlP1.y - 7, 50, 20);
            [@"控制点1" drawInRect:control1MsgRect withAttributes:nil];
    
            CGRect control2MsgRect = CGRectMake(self.controlP2.x + 8, self.controlP2.y - 7, 50, 20);
            [@"控制点2" drawInRect:control2MsgRect withAttributes:nil];
        }
    
        /// 触摸开始
        - (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event {
    
            // 获取触摸点位置
            CGPoint startPoint = [touches.anyObject locationInView:self];
    
            CGRect startR    = CGRectMake(self.startP.x   - 4, self.startP.y   - 4, 8, 8);
            CGRect endR      = CGRectMake(self.endP.x     - 4, self.endP.y     - 4, 8, 8);
            CGRect controlR1 = CGRectMake(self.controlP1.x - 4, self.controlP1.y - 4, 8, 8);
            CGRect controlR2 = CGRectMake(self.controlP2.x - 4, self.controlP2.y - 4, 8, 8);
    
            // 判断当前触摸点
            if (CGRectContainsPoint(startR, startPoint)) {
                self.currentTouchP = 1;
            } else if (CGRectContainsPoint(endR, startPoint)) {
                self.currentTouchP = 2;
            } else if (CGRectContainsPoint(controlR1, startPoint)) {
                self.currentTouchP = 3;
            } else if (CGRectContainsPoint(controlR2, startPoint)) {
                self.currentTouchP = 4;
            }
        }
    
        /// 触摸移动
        - (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event {
    
            // 获取触摸点位置
            CGPoint touchPoint = [touches.anyObject locationInView:self];
    
            // 限制触摸点的边界
    
            if (touchPoint.x < 0) {
                touchPoint.x = 0;
            }
    
            if (touchPoint.x > self.bounds.size.width) {
                touchPoint.x = self.bounds.size.width;
            }
    
            if (touchPoint.y < 0) {
                touchPoint.y = 0;
            }
    
            if (touchPoint.y > self.bounds.size.height) {
                touchPoint.y = self.bounds.size.height;
            }
    
            // 设置当前触摸点的值
            switch (self.currentTouchP) {
                case 1:
                    self.startP = touchPoint;
                    break;
    
                case 2:
                    self.endP = touchPoint;
                    break;
    
                case 3:
                    self.controlP1 = touchPoint;
                    break;
    
                case 4:
                    self.controlP2 = touchPoint;
                    break;
    
                default:
                    break;
            }
    
            // 刷新
            [self setNeedsDisplay];
        }
    
        /// 触摸结束
        - (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event {
    
            // 释放之前的触摸点
            self.currentTouchP = 0;
        }
    
        /// 触摸取消
        - (void)touchesCancelled:(NSSet *)touches withEvent:(nullable UIEvent *)event {
            [self touchesEnded:touches withEvent:event];
        }
    
        @end
  • ViewController.m

        CGRect frame = CGRectMake(20, 50, self.view.bounds.size.width - 40, 400);
    
        QBezierPathView *pathView = [[QBezierPathView alloc] initWithFrame:frame];
    
        pathView.backgroundColor = [UIColor whiteColor];
        pathView.layer.borderWidth = 1;
    
        [self.view addSubview:pathView];
  • 效果

    Quartz2D114Quartz2D115

目录
相关文章
|
iOS开发
iOS 贝塞尔曲线绘制滴水效果
iOS 贝塞尔曲线绘制滴水效果
68 0
|
iOS开发
iOS 利用贝塞尔曲线实现Q弹的下拉刷新
iOS 利用贝塞尔曲线实现Q弹的下拉刷新
61 0
|
iOS开发
ios贝塞尔曲线表格视图
x轴和y轴都是可自定义,曲线的值也是对应的值
102 0
|
iOS开发 MacOS
IOS之Quartz
IOS之Quartz
166 0
IOS之Quartz
|
iOS开发
iOS开发UI篇 - Quartz 2D简单使用
iOS开发UI篇 - Quartz 2D简单使用
iOS开发UI篇 - Quartz 2D简单使用
|
iOS开发
iOS开发UI篇—Quartz2D使用(图形上下文栈)
iOS开发UI篇—Quartz2D使用(图形上下文栈) 一、qurza2d是怎么将绘图信息和绘图的属性绘制到图形上下文中去的? 说明: 新建一个项目,自定义一个view类和storyboard关联后,重写该类中的drowrect方法。
744 0
|
iOS开发
iOS开发UI篇—Quartz2D使用(矩阵操作)
iOS开发UI篇—Quartz2D使用(矩阵操作) 一、关于矩阵操作 1.画一个四边形 通过设置两个端点(长和宽)来完成一个四边形的绘制。 代码: 1 - (void)drawRect:(CGRect)rect 2 { 3 //画四边形 4 //获取图...
729 0
|
API 图形学 iOS开发
IOS开发应用之Quartz 2D学习指南
引用:http://mobile.51cto.com/iphone-284414.htm IOS开发应用之Quartz 2D学习指南是本文要介绍的内容,主要是来学习Quartz 2D的使用方法和操作。
937 0
|
iOS开发
iOS:quartz2D绘图(给图形绘制阴影)
quartz2D既可以绘制原始图形,也可以给原始图形绘制阴影。 绘制阴影时,需要的一些参数:上下文、阴影偏移量、阴影模糊系数 注意:在drawRect:方法中同时调用绘制同一个图形时,在对绘制的图形做绘制阴影处理前,需要先对上下文进行保存,绘制阴影成功后,还要对上下文进行复位,还原为最原始的上下文。
1186 0

热门文章

最新文章

  • 1
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    24
  • 2
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    32
  • 3
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    27
  • 4
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    23
  • 5
    uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
    143
  • 6
    【05】2025年1月首发完整版-篇幅较长-苹果app如何上架到app store完整流程·不借助第三方上架工具的情况下无需花钱但需仔细学习-优雅草央千澈详解关于APP签名以及分发-们最关心的一篇来了-IOS上架app
    232
  • 7
    app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
    90
  • 8
    深入探索iOS开发中的SwiftUI框架
    143
  • 9
    ios样式开关按钮jQuery插件
    58
  • 10
    Android与iOS生态差异深度剖析:技术架构、开发体验与市场影响####
    75