抽屉效果的复杂实现(移动缩放)
- 获取上一次mainV的frame大小
CGRect frame = self.mainV.frame;
- 获取屏幕的宽度和高度
CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
CGFloat screenH = [UIScreen mainScreen].bounds.size.height; - X轴每平移一点,Y轴需要移动
CGFloat offsetY = offsetX * kMaxY / screenW; - 获取上一次的宽度和高度
CGFloat preW = frame.size.width;
CGFloat preH = frame.size.height; - 获取当前的高度
CGFloat curH = preH - 2 * offsetY; - 获取尺寸的缩放比例
CGFloat scale = curH / preH; - 获取当前宽度
CGFloat curW = preW * scale; - 获取当前X和当前Y
frame.origin.x += offsetX;
CGFloat y = (screenH - curH) / 2;
frame.origin.y = y;
frame.size.height = curH;
frame.size.width = curW;
什么是Quartz 2D
- Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统
- 绘图的步骤:
- 获取上下文
- 创建路径(描述路径)
- 把路径添加到上下文
- 渲染上下文
- 通常在drawRect方法里面绘制图形,因为只有在这个方法里面才能获取到跟view的layer相关联的图形上下文
- draw的调用时间:当这个View要显示的时候才会调用drawRect方法绘制图形,其中drawRect方法中的rect参数为当前控件的bounds
drawRect的加载顺序
- viewDidLoad --> viewWillApper --> drawRect -->viewDidApper
基本图形的绘制
- 方法一:原生绘制方法
#pragma mark - 最原始的绘图方式
- (void)drawLine
{
// 1.获取图形上下文
// 目前我们所用的上下文都是以UIGraphics
// CGContextRef Ref:引用 CG:目前使用到的类型和函数 一般都是CG开头 CoreGraphics
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.描述路径
// 创建路径
CGMutablePathRef path = CGPathCreateMutable();
// 设置起点
// path:给哪个路径设置起点
CGPathMoveToPoint(path, NULL, 50, 50);
// 添加一根线到某个点
CGPathAddLineToPoint(path, NULL, 200, 200);
// 3.把路径添加到上下文
CGContextAddPath(ctx, path);
// 4.渲染上下文
CGContextStrokePath(ctx);
}
- 方法二
#pragma mark - 绘图第二种方式
- (void)drawLine1
{
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 描述路径
// 设置起点
CGContextMoveToPoint(ctx, 50, 50);
CGContextAddLineToPoint(ctx, 200, 200);
// 渲染上下文
CGContextStrokePath(ctx);
}
- 方法三
#pragma mark - 绘图第三种方式
- (void)drawLine2
{
// UIKit已经封装了一些绘图的功能
// 贝瑟尔路径
// 创建路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 设置起点
[path moveToPoint:CGPointMake(50, 50)];
// 添加一根线到某个点
[path addLineToPoint:CGPointMake(200, 200)];
// 绘制路径
[path stroke];
// NSLog(@"%@",NSStringFromCGRect(rect));
}
基本图形绘制
- 添加一根线到圆心
[path addLineToPoint:center]; - 封闭路径,关闭路径:从路径的终点到起点
[path closePath]; - 填充:必须是一个完整的封闭路径,默认就会自动关闭路径
[path fill];
重绘下载进度条
- 自定义下载进度条ProgressView,并且声明progress属性,方便控制器向View传递下载进度的值
- 重写ProgressView的drawRect方法,创建贝瑟尔路径
// 注意:drawRect不能手动调用,因为图形上下文我们自己创建不了,只能由系统帮我们创建,并且传递给我们
- (void)drawRect:(CGRect)rect {
// Drawing code
// 创建贝瑟尔路径
CGFloat radius = rect.size.width * 0.5;
CGPoint center = CGPointMake(radius, radius);
CGFloat endA = -M_PI_2 + _progress * M_PI * 2;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius - 2 startAngle:-M_PI_2 endAngle:endA clockwise:YES];
[path stroke];
}
- 重写setProgress方法,每次调用该方法都要对View进行重绘
- (void)setProgress:(CGFloat)progress
{
_progress = progress;
// 重新绘制圆弧
// [self drawRect:self.bounds];
// 重绘,系统会先创建与view相关联的上下文,然后再调用drawRect
[self setNeedsDisplay];
}
画饼图
- 生成随机数组的方法
- (NSArray *)arrRandom
{
int totoal = 100;
NSMutableArray *arrM = [NSMutableArray array];
int temp = 0; // 30 40 30
for (int i = 0; i < arc4random_uniform(10) + 1; i++) {
temp = arc4random_uniform(totoal) + 1;
// 100 1~100
// 随机出来的临时值等于总值,直接退出循环,因为已经把总数分配完毕,没必要在分配。
[arrM addObject:@(temp)];
// 解决方式:当随机出来的数等于总数直接退出循环。
if (temp == totoal) {
break;
}
totoal -= temp;
}
// 100 30 1~100
// 70 40 0 ~ 69 1 ~ 70
// 30 25
// 5
if (totoal) {
[arrM addObject:@(totoal)];
}
return arrM;
}
- 生成随机颜色的方法
- (UIColor *)colorRandom
{
// 0 ~ 255 / 255
// OC:0 ~ 1
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];
}
- 根据数组绘制饼状图的方法
- (void)drawRect:(CGRect)rect {
// Drawing code
NSArray *arr = [self arrRandom];
CGFloat radius = rect.size.width * 0.5;
CGPoint center = CGPointMake(radius, radius);
CGFloat startA = 0;
CGFloat angle = 0;
CGFloat endA = 0;
for (int i = 0; i < arr.count; i++) {
startA = endA;
angle = [arr[i] integerValue] / 100.0 * M_PI * 2;
endA = startA + angle;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
[path addLineToPoint:center];
[[self colorRandom] set];
[path fill];
}
}
绘制柱状图
- 绘制柱状图的方法
- 主要是确定每一个柱体的高度(height)、宽度(width)、左上角横坐标(x)和右上角横坐标(y)四个元素即可
- (void)drawRect:(CGRect)rect {
// Drawing code
NSArray *arr = [self arrRandom];
CGFloat x = 0;
CGFloat y = 0;
CGFloat w = 0;
CGFloat h = 0;
for (int i = 0; i < arr.count; i++) {
w = rect.size.width / (2 * arr.count - 1);
x = 2 * w * i;
h = [arr[i] floatValue] / 100.0 * rect.size.height;
y = rect.size.height - h;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, w, h)];
[[self colorRandom] set];
[path fill];
}
}
绘制文字和图片
- 通过drawAtPoint:withAttributes方法绘制文本
- (void)attrText
{
// 绘制文字
NSString *str = @"asfdsfsdf";
// 文字的起点
// Attributes:文本属性
NSMutableDictionary *textDict = [NSMutableDictionary dictionary];
// 设置文字颜色
textDict[NSForegroundColorAttributeName] = [UIColor redColor];
// 设置文字字体
textDict[NSFontAttributeName] = [UIFont systemFontOfSize:30];
// 设置文字的空心颜色和宽度
textDict[NSStrokeWidthAttributeName] = @3;
textDict[NSStrokeColorAttributeName] = [UIColor yellowColor];
// 创建阴影对象
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor greenColor];
shadow.shadowOffset = CGSizeMake(4, 4);
shadow.shadowBlurRadius = 3;
textDict[NSShadowAttributeName] = shadow;
// 富文本:给普通的文字添加颜色,字体大小
[str drawAtPoint:CGPointZero withAttributes:textDict];
}
- 通过label绘制文本
- (void)drawText
{
// 绘制文字
NSString *str = @"asfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdf";
// 不会换行
// [str drawAtPoint:CGPointZero withAttributes:nil];
[str drawInRect:self.bounds withAttributes:nil];
}
- 绘制图片
- (void)drawRect:(CGRect)rect {
// Drawing code
// 超出裁剪区域的内容全部裁剪掉
// 注意:裁剪必须放在绘制之前,若放在绘制之后将没有裁剪效果
UIRectClip(CGRectMake(0, 0, 50, 50));
UIImage *image = [UIImage imageNamed:@"001"];
// 默认绘制的内容尺寸跟图片尺寸一样大
// [image drawAtPoint:CGPointZero];
// [image drawInRect:rect];
// 绘图
[image drawAsPatternInRect:rect];
}
雪花(定时器操作)
-
如果在绘图的时候需要用到定时器,通常NSTimer很少用于绘图,因为调度优先级比较低,并不会准时调用,所以在展示的时候会有卡顿的现象
-
通常使用CADisplayLink完成绘图过程中的定时任务操作,因为其每次屏幕刷新的时候就会调用,屏幕一般一秒刷新60次
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeChange)];// 添加主运行循环 [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
-
调用[self setNeedsDisplay]方法并不会马上调用drawRect方法,其实这个方法只是给当前控件添加刷新的标记,等下一次屏幕刷新的时候才会调用drawRect方法,其和CaDisplayLink定时器实现了同步,所以看不到卡顿的现象
图形上下文状态栈
- 如果以后用贝瑟尔绘制图形[path stroke],上下文的状态由贝瑟尔路径决定
- (void)drawRect:(CGRect)rect {
// Drawing code
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.描述路径
// 第一根
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 125)];
[path addLineToPoint:CGPointMake(240, 125)];
// 把路径添加到上下文
// .CGPath 可以UIkit的路径转换成CoreGraphics路径
CGContextAddPath(ctx, path.CGPath);
// 保存一份上下文的状态
CGContextSaveGState(ctx);
// 设置上下文状态
CGContextSetLineWidth(ctx, 10);
[[UIColor redColor] set];
// 渲染上下文
CGContextStrokePath(ctx);
// 第二根
// 2.描述路径
// 第一根
path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(125, 10)];
[path addLineToPoint:CGPointMake(125, 240)];
// 把路径添加到上下文
// .CGPath 可以UIkit的路径转换成CoreGraphics路径
CGContextAddPath(ctx, path.CGPath);
// 还原状态
CGContextRestoreGState(ctx);
// // 设置上下文状态
// CGContextSetLineWidth(ctx, 1);
//
// [[UIColor blackColor] set];
// 渲染上下文
CGContextStrokePath(ctx);
}
上下文矩阵操作
- 通过上下文矩阵操作可以将绘制好的图形,进行平移、放大和旋转操作
- 注意:矩阵操作必须要在添加路径之前,否则矩阵操作将无效
- (void)drawRect:(CGRect)rect {
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.描述路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-100, -50, 200, 100)];
[[UIColor redColor] set];
// 上下文矩阵操作
// 注意:矩阵操作必须要在添加路径之前
// 平移
CGContextTranslateCTM(ctx, 100, 50);
// 缩放
CGContextScaleCTM(ctx, 0.5, 0.5);
// 旋转
CGContextRotateCTM(ctx, M_PI_4);
// 3.把路径添加上下文
CGContextAddPath(ctx, path.CGPath);
[[UIColor redColor] set];
// 4.渲染上下文
CGContextFillPath(ctx);
}
网友评论