您的位置:首页 > 产品设计 > UI/UE

iOS开发——UI进阶篇(十六)Quartz2D实战小例子

2015-08-04 22:02 716 查看

一、画线

只有在drawRect中才能获取到跟view相关联的上下文

- (void)drawRect:(CGRect)rect {}

一条线

// 1.获取跟当前View相关联的layer上下文(画板)
// 总结:目前获取的所有上下文都是以UIGraphics开头
// CGContextRef:上下文类型
// CG:CoreGraphics Ref:引用
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.绘制内容,拼接路径
// 创建贝瑟尔路径,因为里面已经封装好了很多路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 绘制一条直线
// 设置起点
[path moveToPoint:CGPointMake(50, 50)];
// 添加一根线到某个点
[path addLineToPoint:CGPointMake(200, 200)];

// 3.把拼接好的路径添加到上下文(画板)
// UIBezierPath -> CGPath,直接.CGPath
CGContextAddPath(ctx, path.CGPath);

// 4.渲染上下文,就把内容显示到view
// 只要跟上下文有关系的东西,都以CGContext开头
CGContextStrokePath(ctx);


两条线

// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路径,一个路径中可以保存多条线段
UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(10, 10)];

[path addLineToPoint:CGPointMake(20, 20)];

// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);

// 一根线对应一个路径,只要绘制的线不连接,最好使用一根线对应一个路径的方法
path = [UIBezierPath bezierPath];
// 拼接另一根直线
// 默认下一根线的起点就是上一根线的终点
// 设置第二根线的起点
//    [path moveToPoint:CGPointMake(20, 20)];
// 如果想要绘制不连接的线,重新设置起点
[path moveToPoint:CGPointMake(50, 50)];

[path addLineToPoint:CGPointMake(20, 200)];

// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);

// 4.渲染上下文
CGContextStrokePath(ctx);


绘制曲线

// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路径
UIBezierPath *path = [UIBezierPath bezierPath];

// 设置起点
[path moveToPoint:CGPointMake(10, 125)];

// 描述曲线
[path addQuadCurveToPoint:CGPointMake(240, 125) controlPoint:CGPointMake(125, 240)];

[path addLineToPoint:CGPointMake(10, 125)];

// 3.添加路径到上下文
CGContextAddPath(ctx, path.CGPath);

// 设置绘图状态,一定要再渲染之前
// 设置颜色
[[UIColor redColor] setStroke];

// 设置线段的宽度
CGContextSetLineWidth(ctx, 15);
// 设置线段的顶角样式
CGContextSetLineCap(ctx, kCGLineCapRound);

// 设置连接样式
CGContextSetLineJoin(ctx, kCGLineJoinRound);

// 4.渲染上下文
CGContextStrokePath(ctx);


画扇形

// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路径
CGPoint center = CGPointMake(self.bounds.size.width * 0.5, self.bounds.size.height * 0.5);
// 画扇形
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:100 startAngle:0 endAngle:M_PI_2 clockwise:YES];

[path addLineToPoint:center];

[path closePath];

// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);

// 设置绘图的状态
[[UIColor redColor] setFill];

[[UIColor greenColor] setStroke];

CGContextSetLineWidth(ctx, 5);

// 4.渲染上下文
CGContextDrawPath(ctx, kCGPathFillStroke);




注意点:

// 关闭路径:从路径的终点连接到起点
//    [path closePath];


// 如果路径不是封闭的,默认会关闭路径
[path fill];


二、重绘(下载进度)



根据下载的进度来画圆 (slider当做下载进度)

核心代码

自定义view

/******************** ProgressView*****************/
#import <UIKit/UIKit.h>

@interface ProgressView : UIView

// 下载进度
@property (nonatomic, assign) CGFloat progress;

@end

#import "ProgressView.h"

@implementation ProgressView

- (void)setProgress:(CGFloat)progress
{
_progress = progress;

// drawRect不能手动调用
//    [self drawRect:self.bounds];
// 重绘
[self setNeedsDisplay];
}

// drawRect只会调用一次
// drawRect只能系统调用,而且每次调用之前系统都会创建一个跟View相关联的上下文传递给你
- (void)drawRect:(CGRect)rect {

// 画圆弧
CGPoint center = CGPointMake(self.bounds.size.width * 0.5, self.bounds.size.width * 0.5);
CGFloat radius = self.bounds.size.width * 0.5 - 2;
CGFloat startA = -M_PI_2;
CGFloat endA = -M_PI_2 + _progress * M_PI * 2;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

[path stroke];

}

@end

/******************** ProgressView*****************/
#import "ViewController.h"

#import "ProgressView.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UILabel *labelView;
@property (weak, nonatomic) IBOutlet ProgressView *progressView;

@end

@implementation ViewController
- (IBAction)valueChange:(UISlider *)sender {

// %在stringWithFormat里面有特殊含义,需要转义 %% 相当于一个%
_labelView.text = [NSString stringWithFormat:@"%.2f%%",sender.value * 100];
_progressView.progress = sender.value;
}


三、画饼图



自定义view

#import "PieView.h"

@implementation PieView

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {

NSArray *data = @[@25,@25,@20,@30];

CGPoint center = CGPointMake(self.bounds.size.width * 0.5, self.bounds.size.height * 0.5);
CGFloat radius = self.bounds.size.width * 0.5;
CGFloat startA = 0;
CGFloat endA = 0;
CGFloat angle = 0;

for (NSNumber *num in data) {
// 画一个扇形
startA = endA;
angle = [num intValue] / 100.0 * M_PI * 2;
endA = startA + angle;

UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

[path addLineToPoint:center];

// set:同时设置描边和填充颜色
[[self randomColor] set];

[path fill];
}
}

// 随机颜色
- (UIColor *)randomColor
{
CGFloat r = arc4random_uniform(256) / 255.0;
CGFloat g = arc4random_uniform(256) / 255.0;
CGFloat b = arc4random_uniform(256) / 255.0;
return [UIColor colorWithRed:r green:g blue:b alpha:1];
}

@end


四、UIKit绘图演练

1)画文字

NSString *str = @"hello!";

// Attributes:属性
// 给一个字符串添加属性,可以叫富文本,颜色,字体大小,空心,阴影

// 利用这个属性字典给文本添加属性
NSMutableDictionary *strAttr = [NSMutableDictionary dictionary];
// key,value
// 如何找到设置文本的属性key
// 描述了字体
strAttr[NSFontAttributeName] = [UIFont boldSystemFontOfSize:50];

// 设置描边的颜色和宽度
strAttr[NSStrokeWidthAttributeName] = @1;

strAttr[NSStrokeColorAttributeName] = [UIColor redColor];

NSShadow *shadow = [[NSShadow alloc] init];

shadow.shadowColor = [UIColor yellowColor];

shadow.shadowOffset = CGSizeMake(10, 10);

shadow.shadowBlurRadius = 5;

// 阴影
strAttr[NSShadowAttributeName] = shadow;

// 文字颜色
strAttr[NSForegroundColorAttributeName] = [UIColor redColor];

[str drawAtPoint:CGPointZero withAttributes:strAttr];


2)画图片,裁剪

- (void)drawRect:(CGRect)rect {

// UIKit框架封装的方法绘制内容

// 裁剪,rect裁剪的区域

// 文字和图片
UIImage *image = [UIImage imageNamed:@"001"];

// 裁剪注意点:一定要放在绘图之前
// 超出裁剪区域的内容会被裁剪掉
UIRectClip(CGRectMake(0, 0, 50, 50));

// 绘制的内容跟图片一样大
[image drawAtPoint:CGPointZero];

// 把绘制的内容控制到某个区域内
[image drawInRect:CGRectMake(0, 0, 100, 100)];
//    [image drawAsPatternInRect:rect];
}


五、雪花(定时器)



自定义SnowView

#import "SnowView.h"

@implementation SnowView

- (void)awakeFromNib
{
// 设置定时器
//    [NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:@selector(setNeedsDisplay) userInfo:nil repeats:YES];

// 0.1 setNeedsDisplay 绑定一个标识,等待下次刷新的时候才会调用drawRect方法
// 0.15 屏幕的刷新时间

// 定时器
// 每次屏幕刷新的时候就会调用,屏幕一秒刷新60次
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)];

// 只要把定时器添加到主运行循环就能自动执行
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

// setNeedsDisplay:底层并不会马上调用drawRect,只会给当前的控件绑定一个刷新的标识,每次屏幕刷新的时候,就会把绑定了刷新(重绘)标识的控件重新刷新(绘制)一次,就会调用drawRect去重绘

// 如果以后每隔一段时间需要重绘,一般不使用NSTimer,使用CADisplayLink,不会刷新的时候有延迟
}

- (void)drawRect:(CGRect)rect {
// Drawing code

static CGFloat snowY = 0;

UIImage *image = [UIImage imageNamed:@"雪花"];

[image drawAtPoint:CGPointMake(0, snowY)];

snowY += 10;

if (snowY > rect.size.height) {
snowY = 0;
}

NSLog(@"%f",snowY);
}


六、图形上下文状态栈

自定义DrawView

#import "DrawView.h"

@implementation DrawView

- (void)drawRect:(CGRect)rect {
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路径
UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(10, 125)];

[path addLineToPoint:CGPointMake(240, 125)];

// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);

// 保存当前上下文的默认状态
CGContextSaveGState(ctx);

// 设置状态
[[UIColor redColor] set];
CGContextSetLineWidth(ctx, 20);

// 渲染上下文
CGContextStrokePath(ctx);

// 创建第二根路径
path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(125, 10)];
[path addLineToPoint:CGPointMake(125, 240)];

// 添加到上下文
CGContextAddPath(ctx, path.CGPath);

// 恢复下上下文状态
// 取出之前的保存的状态覆盖掉当前的状态
CGContextRestoreGState(ctx);
//    [[UIColor blackColor] set];
//    CGContextSetLineWidth(ctx, 1);

// 4.渲染上下文到view的layer
// 在渲染之前,系统会查看下上下文的状态,根据状态去渲染
CGContextStrokePath(ctx);
}




可以根据运行结果看到,恢复下上下文状态,就可以让第二条线和第一条线状态不一样,从而达到多样化

七、矩阵操作

#import "DrawView.h"

@implementation DrawView

- (void)drawRect:(CGRect)rect {

// 矩阵操作:上下文可以做平移,旋转,缩放,开发中用的比较多是旋转.
// 获取跟view相关联的上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 平移
CGContextTranslateCTM(ctx, 150, 200);

// 旋转
CGContextRotateCTM(ctx, M_PI_4);

// 缩放
CGContextScaleCTM(ctx, 0.5, 0.5);

UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-50, -100, 100, 200)];

[[UIColor redColor] set];

[path fill];

}
@end


八、图片水印

- (void)viewDidLoad {
[super viewDidLoad];
// 给一张图片添加文字或者Logo,生成一张新的带有文字或者Logo图片

// 绘制图片到位图上下文,然后再利用位图上下文生成一张新的图片

// 加载图片
UIImage *image = [UIImage imageNamed:@"小黄人"];

// 1.开启位图上下文
// size:上下文的尺寸
// opaque:不透明,总结:在上下文中一般都是设置不透明度,控件才是设置透明度
// scale: 0表示不缩放

UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
//    UIGraphicsBeginImageContextWithOptions(<#CGSize size#>, <#BOOL opaque#>, <#CGFloat scale#>)

// 绘制图片
[image drawAtPoint:CGPointZero];

// 绘制文字
NSString *str = @"超神五杀怪我咯";
[str drawAtPoint:CGPointZero withAttributes:@{NSForegroundColorAttributeName : [UIColor redColor]}];

// 从上下文内容中生成一张图片
image = UIGraphicsGetImageFromCurrentImageContext();

// 结束上下文
UIGraphicsEndImageContext();

// 把图片写入到桌面

// image -> NSData
// 把图片生成一个png格式的二进制数据
// png已经高清图片格式
//    NSData *data = UIImagePNGRepresentation(image);
// compressionQuality:图片质量

// jpg 图片质量比 png 低
NSData *data = UIImageJPEGRepresentation(image, 0.00001);

[data writeToFile:@"/Users/chg/Desktop/image.jpg" atomically:YES];

}


两种格式的照片质量清晰度不一样



九、图片剪裁

创建一个分类

指定一个图片名称,和圆环宽度和颜色,生成一张带有圆环的圆形图片

/**************UIImage+Image.h****************/
#import <UIKit/UIKit.h>

@interface UIImage (Image)

// 指定一个图片名称,和圆环宽度和颜色,生成一张带有圆环的圆形图片
+ (instancetype)imageCirCleWithCircleColor:(UIColor *)color CircleBorder:(CGFloat)border name:(NSString *)imageName;

@end

/**************UIImage+Image.m****************/
#import "UIImage+Image.h"

@implementation UIImage (Image)

+ (instancetype)imageCirCleWithCircleColor:(UIColor *)color CircleBorder:(CGFloat)border name:(NSString *)imageName
{
// 裁剪带圆环的图片
CGFloat borderWH = border;

// 加载图片
UIImage *image = [UIImage imageNamed:imageName];

CGRect bigContextRect = CGRectMake(0, 0, image.size.width + 2 * borderWH, image.size.height + 2 * borderWH);

// 1.开启位图上下文
UIGraphicsBeginImageContextWithOptions(bigContextRect.size, NO, 0);

// 2.画大圆
UIBezierPath *bigCirclePath = [UIBezierPath bezierPathWithOvalInRect:bigContextRect];

// 设置大圆颜色
[color set];

[bigCirclePath fill];

// 3.设置裁剪区域
// 3.1 先描述裁剪区域
UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(borderWH, borderWH, image.size.width, image.size.height)];
[clipPath addClip];

// 4.绘制图片
[image drawAtPoint:CGPointMake(borderWH, borderWH)];

// 5.从上下文内容中生成一张图片
image = UIGraphicsGetImageFromCurrentImageContext();

// 6.结束上下文
UIGraphicsEndImageContext();

return image;
}
@end




十、屏幕截屏

在storyboard随便拖些控件 截取屏幕



核心代码

- (void)viewDidLoad {
[super viewDidLoad];

// 1.开启位图上下文
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);

// 获取当前的位图上下文
CGContextRef ctx =  UIGraphicsGetCurrentContext();

// 2.往上下文中填充内容,把控制器的view的内容画上去,应该是把控制器view上的图层画到上下文上.
// 获取控制器view的图层
CALayer *layer = self.view.layer;

// 把layer中内容渲染到上下文中,图层只能渲染,不能绘制
[layer renderInContext:ctx];
//    [layer drawInContext:ctx];

// 3.从上下文中取出图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

// 4.结束上下文
UIGraphicsEndImageContext();

NSData *data = UIImagePNGRepresentation(image);

[data writeToFile:@"/Users/chg/Desktop/view.png" atomically:YES];

}


十一、图片截屏

类似qq截图的功能



先用storyboard给控件(图片)添加手势



核心代码

// 蒙板
@property (nonatomic, weak) UIView *cover;
// 记录下截屏一开始的位置
@property (nonatomic, assign) CGPoint oriP;

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

- (UIView *)cover
{
if (_cover == nil) {
UIView *view = [[UIView alloc] init];

view.backgroundColor = [UIColor blackColor];
view.alpha = 0.5;

_cover = view;

[self.view addSubview:view];

}
return _cover;
}

- (IBAction)pan:(UIPanGestureRecognizer *)sender {
// 获取下当前的触摸
CGPoint curP = [sender locationInView:_imageView];
if (sender.state == UIGestureRecognizerStateBegan) {
// 记录下一开始的位置
_oriP = curP;
}

// 计算下黑色蒙版的frame
CGFloat w = curP.x - _oriP.x;
CGFloat h = curP.y - _oriP.y;

self.cover.frame = CGRectMake(_oriP.x, _oriP.y, w, h);

if (sender.state == UIGestureRecognizerStateEnded) { // 手指抬起

// 裁剪图片,生成一张新图片

// 开启位图上下文
UIGraphicsBeginImageContextWithOptions(_imageView.bounds.size, NO, 0);

// 设置裁剪区域
UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.cover.frame];
[path addClip];

// 绘制图片
[_imageView.layer renderInContext:UIGraphicsGetCurrentContext()];

// 生成图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

// 关闭上下文
UIGraphicsEndImageContext();

_imageView.image = image;

[self.cover removeFromSuperview];
}
}


十二、图片擦除



首先准备两张图片



用第一张盖住第二张

给上面的图片添加pan(拖拽)手势

核心代码

// 只要用户手指在图片上拖动.就会调用
- (IBAction)pan:(UIPanGestureRecognizer *)sender {
// 拖动的时候,擦除图片的某一部分

// 获取手指的触摸点
CGPoint curP = [sender locationInView:sender.view];

// 计算擦除的frame
CGFloat wh = 30;
CGFloat x = curP.x - wh * 0.5;
CGFloat y = curP.y - wh * 0.5;

CGRect frame = CGRectMake(x, y, wh, wh);

// 开启位图上下文
UIGraphicsBeginImageContextWithOptions(sender.view.bounds.size, NO, 0);

// 获取当前的上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();

// 把控件上的内容渲染到上下文
[sender.view.layer renderInContext:ctx];

// 清除上下文中某一部分的内容
CGContextClearRect(ctx, frame);

// 生成一张新的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

// 关闭上下文
UIGraphicsEndImageContext();

// 重新显示到UIImageView
UIImageView *imageV = (UIImageView *)sender.view;

imageV.image = image;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: