iOS开发之自定义view—Quartz2D

之前的文章介绍了数据的传递和存储,这篇文章就介绍显示数据的控件。iPhone之所以这么受欢迎,其中有一个原因是因为它的UI界面非常美观。iOS提供了UIKit框架,里面有各种各样的UI控件。

这里有利用Quartz2D生成的图片(包括裁剪圆形图片,截图,水印图片):https://github.com/shihuaixing/ClipCircleImageDemo.git

UILabel:显示文字

UIImageVIew:显示图片

UIButton:同时显示图片和文字,还能点击

等等。

这些控件进行拼拼凑凑是能够搭建和实现一些简单、常见的UI界面,并显示数据的。

但是有些UI界面极其复杂、而且比较有个性,用普通的UI控件是无法实现的,这个时候可以利用Quartz2D技术奖控件内部的结构画出来,就向自定义控件的样子。

其实,iOS中大部分控件的内容都是通过Quarz2D画出来的。

因此,Quarz2D在iOS开发中很重要的一个价值就是:自定义view(自定义控件)。

我们来简单了解一下关于Quarz2D的知识。

Quarz2D:是一个二维绘图引擎,同时支持iOS和Mac系统。

Quarz2D的API是纯C语言的。

Quarz2D的API来自于Core Graphics框架

Quarz2D函数类型和函数基本都以CG为前缀:

CGContextRef

CGPathRef

CGContextStrokePath(ctx)

Quarz2D能完成的简单的工作:

1) 绘制图形:线条/三角形/矩形/圆/弧等

2) 绘制文字

3) 绘制/生成图片

4)读取/生成PDF

5)图/裁剪图片

6)定义控件

7)等等

下面来看一下利用Quarz2D实现的一些效果:

1、图片裁剪:

2、手势解锁:

下面我们在使用Quarz2D之前,简单认识一些概念:

1)图形上下文(Graphics Context):是一个CGContextRef类型的数据

a.    作用:

保存绘图信息,绘图状态;

决定绘制的输出目标(绘制到什么地方)

(输出目标客源是PDF文件、Bitmap或者显示器的窗口上)

b.    Quarz2D提供了以下几种类型的Graphics Context:

·  Bitmap Graphics Context

·  PDF Graphics Context

·  Window Graphics Context

·  Layer Graphics Context

·  Printer Graphics Context


注意:图形上下文(Graphics Context)是一个很重要的类型!

了解了图形上下文(GraphicsContext),那我们如何利用Quarz2D自定义view(自定义控件)?

如何利用Quarz2D绘制内容到view上:

1)首先,得有图形上下文,因为它能保存绘图信息,并且决定这绘制到什么地方去

2)其次,那个图形上下文必须跟view想关联,才能讲内容绘制到view上

自定义view的步骤:

1)新建一个类,继承自UIView

2)实现-(void)drawRect:方法,然后在这个方法中

a.    取得跟当前view相关联的图形上下文

b.    绘制相应的图形内容

c.    利用图形上下文将绘制的所有内容渲染显示到view上

那大家可能会要问,为什么要实现drawRect:方法才能将绘图显示到view上呢?最重要的一点:因为在drawRect:方法中才能取到跟view相关联的图形上下文。

drawRect: 方法在什么时候被执行呢?

了解:

1)view内部有个layer(图层)属性,drawRect:方法中取得的是一个Layer GraphicsContext,因此,绘制的内容其实是绘制到viewlayer上去了。

2)view之所以能显示东西,完全是因为它内部的layer(图层)

下面通过代码显示Quarz2D的功能:

创建新项目,在控制器view上拖一个自定义view(redView),创建自定义view类(HXView)。将自定义的view的类设置为HXView。

1、画线、画圆、画弧

- (void)drawBasisGraphics {
    
    /***************画线*****************/
    // 获得图形上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    // 画线
    // 1.起点
    CGContextMoveToPoint(ctx, 10, 10);
    // 添加线段
    CGContextAddLineToPoint(ctx, 100, 100);
    
    
    CGContextAddLineToPoint(ctx, 200, 30);
    
    // 设置线宽
    CGContextSetLineWidth(ctx, 10);
    // 设置线颜色
    CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
    // 设置两头部样式
    CGContextSetLineCap(ctx, kCGLineCapRound);
    // 设置连接处样式
    CGContextSetLineJoin(ctx, kCGLineJoinRound);
    
    // 渲染并显示到view上
    CGContextStrokePath(ctx);
    
    
    
    /***************画圆*****************/
    
    // 1.通过椭圆画出圆
    // 1)(不填充)
//    CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 100));
//    
//    // 设置线宽
//    CGContextSetLineWidth(ctx, 10);
//    // 设置线颜色
//    CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
//    
//    // 渲染并显示到view上
//    CGContextStrokePath(ctx);
    
    // 2)(填充)
//    CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 100));
//    
//    // 设置线颜色
//    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);
//    
//    // 渲染并显示到view上
//    CGContextFillPath(ctx);
    
    
    // 2.根据圆点、半径(可以画圆弧)
    // 1)不填充
    CGContextAddArc(ctx, 100, 100, 50, 0, M_PI_2, 0);
    // CGContextAddArc: 参数说明
    /*
     CGContextRef c:图形上下文
     CGFloat x, CGFloat y:圆点坐标
     CGFloat radius:圆的半径
     CGFloat startAngle:画圆的开始角度(逆时针为负,顺时针为正)
     CGFloat endAngle:画圆的结束角度
     int clockwise:画图方向,0:顺时针,1:逆时针
     */
    // 设置线颜色
    CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
    
    // 渲染并显示到view上
    CGContextStrokePath(ctx);
    
    // 2)填充
//    CGContextAddArc(ctx, 100, 100, 50, 0, M_PI_2, 0);
//    // 设置线颜色
//    CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);
//    
//    // 渲染并显示到view上
//    CGContextFillPath(ctx);

    
    
    /***************画弧*****************/
    // 当前点
    CGContextMoveToPoint(ctx, 50, 100);
    CGContextAddCurveToPoint(ctx, 0, 0, 100, 100, 50, 0);
    
    // set : 同时设置为实心和空心颜色
    // setStroke : 设置空心颜色
    // setFill : 设置实心颜色
    [[UIColor whiteColor] set];
    
    CGContextStrokePath(ctx);
    
    
}

2、画图,并裁剪

// 画图
- (void)drawImage:(CGRect)rect {
    // 获得图形上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    UIImage *image = [UIImage imageNamed:@"008.jpg"];
    
//    [image drawAsPatternInRect:rect];// 平铺
//    [image drawInRect:rect]; // 拉伸
    [image drawAtPoint:CGPointMake(0, 0)];// 从父控件的0,0点开始画图
    
    // 渲染显示到view上
    CGContextStrokePath(ctx);
}

// 裁剪圆形图片
- (void)drawCircleImage:(CGRect)rect {
    // 获得图形上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    UIImage *image = [UIImage imageNamed:@"008.jpg"];
    
    // 1.画圆
    CGContextAddEllipseInRect(ctx, CGRectMake((rect.size.width - image.size.width) * 0.5, (rect.size.height - image.size.height) * 0.5, image.size.height, image.size.width));
    // 超出该圆的内容全部裁剪掉
    CGContextClip(ctx);// 裁剪当前图形上下文
    CGContextClipToRect(ctx, CGRectMake((rect.size.width - image.size.width) * 0.5, (rect.size.height - image.size.height) * 0.5, image.size.height, image.size.width)); // 裁剪范围
    
    // 画图
    [image drawAtPoint:CGPointMake((rect.size.width - image.size.width) * 0.5, (rect.size.height - image.size.height) * 0.5)];
    
    CGContextSetLineWidth(ctx, 10);
    // 渲染显示到view上
    CGContextStrokePath(ctx);
}

3、画文字(使用Quartz2D画出来的文字是反着的,跟坐标系有关)

- (void)drawText:(CGRect)rect {
    
    NSString *textString = @"哈哈哈哈shx";
    NSMutableDictionary *attris = [NSMutableDictionary dictionary];
    attris[NSFontAttributeName] = [UIFont systemFontOfSize:20];
    attris[NSForegroundColorAttributeName] = [UIColor whiteColor];
    [textString drawInRect:rect withAttributes:attris];
}

下面我们来做一个画板:


清空:清除画板上的所有内容;

回退:清除在画板上画出的最后一条线

 

创建新项目,在控制器view上拖一个自定义view(redView),创建自定义view类(HXView)。将自定义的view的类设置为HXView。

因为手指要在view上触摸才能有事件发生,所以要在HXView.m文件中重写如下方法:

/**
 *  手指开始触摸view的时候调用
 *  在该方法中可以确定线的起点
 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
	
}

/**
 *  手指在view上移动时调用
 *  在该方法中连接其他点
 */
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
}

/**
 *  手指触摸结束时候调用
 *  在该方法中处理一条路径画完之后的事情
 */
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
}

先来了解一下思路:

每当手指开始触摸view的时候就会调用touchesBegan方法,每次调用该方法时就创建一个新的路径(pathn个点组成),把touchesBegan获得的点(起点)添加到这条新的路径中(数组存储点);当手指在view上移动的时候就会调用touchesMoved方法,每次调用该方法就会返回手指所在的位置,把该位置添加到path中;当手指在view上触摸结束的时候就会调用touchesEnded方法,在该方法中项path中添加最后的一个点。至此,path路径上的所有点都被存储在path中了。现在可以利用viewdrawRect:方法开始绘制路径了。

           由于画板可能会绘制很对条路径,所以我们也要把所有的路径存起来。(数组存储路径)

注意:每次向path中添加点的时候都要主动调用setNeedsDisplay方法,该方法会调用drawRect:方法随时绘制路径。

现在来实现这些方法

在touchesBegan方法中,能获得手指开始触摸时候的位置(一个路径的起点)

/**
 *  手指开始触摸view的时候调用
 *  在该方法中可以确定线的起点
 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    // 获得开始触摸的点(起点)
    CGPoint startPoint = [self pointWithTouch:touches];
    // 存储新路径上的所有的点
    NSMutableArray *currentPath = [NSMutableArray array];
    
    // 新路径存储新路径的第一个点(新路径起点)
    [currentPath addObject:[NSValue valueWithCGPoint:startPoint]];
    // 将新路径存储到数组中
    [self.paths addObject:currentPath];
    
    // 重新绘制
    [self setNeedsDisplay];
}

获得点的方法

- (CGPoint)pointWithTouch:(NSSet * )touches {
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:touch.view];
    return point;
}

手指开始移动:

/**
 *  手指在view上移动时调用
 *  在该方法中连接其他点
 */
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    // 获取其他点
    CGPoint otherPoint = [self pointWithTouch:touches];
    
    
    // 取出最后一个路径(当前正在画的路径)
    NSMutableArray *currentPath = [self.paths lastObject];
    // 存储其他点
    [currentPath addObject:[NSValue valueWithCGPoint:otherPoint]];
    
    // 重新绘制
    [self setNeedsDisplay];
    
}

手指触摸事件结束:(考虑为什么只要调用touchesMoved方法)

/**
 *  手指触摸结束时候调用
 *  在该方法中处理一条路径画完之后的事情
 */
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    // 手指离开后,也是连接点
    [self touchesMoved:touches withEvent:event];
    
}

回退功能(移除存储路径的数组的最后一个元素):

- (void)revoke {
    [self.paths removeLastObject];
    [self setNeedsDisplay];   
}

清空功能(移除存储路径的数组的所有元素);

- (void)clear {
    [self.paths removeAllObjects];
    [self setNeedsDisplay];
}
注意:每次改变路径内容的时候都要调用setNeedsDisplay方法!

效果:


回退:

清空:

只要自定义一个view,实现它的drawRect:方法,就可以在这个view上画出你想要的内容。大家可以试着实现以下前途的手势解锁功能的界面。(以上部分图片引用自mj老师)




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值