抽屜效果的復(fù)雜實(shí)現(xiàn)(移動(dòng)縮放)
- 獲取上一次mainV的frame大小
CGRect frame = self.mainV.frame;
- 獲取屏幕的寬度和高度
CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
CGFloat screenH = [UIScreen mainScreen].bounds.size.height; - X軸每平移一點(diǎn),Y軸需要移動(dòng)
CGFloat offsetY = offsetX * kMaxY / screenW; - 獲取上一次的寬度和高度
CGFloat preW = frame.size.width;
CGFloat preH = frame.size.height; - 獲取當(dāng)前的高度
CGFloat curH = preH - 2 * offsetY; - 獲取尺寸的縮放比例
CGFloat scale = curH / preH; - 獲取當(dāng)前寬度
CGFloat curW = preW * scale; - 獲取當(dāng)前X和當(dāng)前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是一個(gè)二維繪圖引擎,同時(shí)支持iOS和Mac系統(tǒng)
- 繪圖的步驟:
- 獲取上下文
- 創(chuàng)建路徑(描述路徑)
- 把路徑添加到上下文
- 渲染上下文
- 通常在drawRect方法里面繪制圖形,因?yàn)橹挥性谶@個(gè)方法里面才能獲取到跟view的layer相關(guān)聯(lián)的圖形上下文
- draw的調(diào)用時(shí)間:當(dāng)這個(gè)View要顯示的時(shí)候才會(huì)調(diào)用drawRect方法繪制圖形,其中drawRect方法中的rect參數(shù)為當(dāng)前控件的bounds
drawRect的加載順序
- viewDidLoad --> viewWillApper --> drawRect -->viewDidApper
基本圖形的繪制
- 方法一:原生繪制方法
#pragma mark - 最原始的繪圖方式
- (void)drawLine
{
// 1.獲取圖形上下文
// 目前我們所用的上下文都是以UIGraphics
// CGContextRef Ref:引用 CG:目前使用到的類(lèi)型和函數(shù) 一般都是CG開(kāi)頭 CoreGraphics
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.描述路徑
// 創(chuàng)建路徑
CGMutablePathRef path = CGPathCreateMutable();
// 設(shè)置起點(diǎn)
// path:給哪個(gè)路徑設(shè)置起點(diǎn)
CGPathMoveToPoint(path, NULL, 50, 50);
// 添加一根線(xiàn)到某個(gè)點(diǎn)
CGPathAddLineToPoint(path, NULL, 200, 200);
// 3.把路徑添加到上下文
CGContextAddPath(ctx, path);
// 4.渲染上下文
CGContextStrokePath(ctx);
}
- 方法二
#pragma mark - 繪圖第二種方式
- (void)drawLine1
{
// 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 描述路徑
// 設(shè)置起點(diǎn)
CGContextMoveToPoint(ctx, 50, 50);
CGContextAddLineToPoint(ctx, 200, 200);
// 渲染上下文
CGContextStrokePath(ctx);
}
- 方法三
#pragma mark - 繪圖第三種方式
- (void)drawLine2
{
// UIKit已經(jīng)封裝了一些繪圖的功能
// 貝瑟爾路徑
// 創(chuàng)建路徑
UIBezierPath *path = [UIBezierPath bezierPath];
// 設(shè)置起點(diǎn)
[path moveToPoint:CGPointMake(50, 50)];
// 添加一根線(xiàn)到某個(gè)點(diǎn)
[path addLineToPoint:CGPointMake(200, 200)];
// 繪制路徑
[path stroke];
// NSLog(@"%@",NSStringFromCGRect(rect));
}
基本圖形繪制
- 添加一根線(xiàn)到圓心
[path addLineToPoint:center]; - 封閉路徑,關(guān)閉路徑:從路徑的終點(diǎn)到起點(diǎn)
[path closePath]; - 填充:必須是一個(gè)完整的封閉路徑,默認(rèn)就會(huì)自動(dòng)關(guān)閉路徑
[path fill];
重繪下載進(jìn)度條
- 自定義下載進(jìn)度條ProgressView,并且聲明progress屬性,方便控制器向View傳遞下載進(jìn)度的值
- 重寫(xiě)ProgressView的drawRect方法,創(chuàng)建貝瑟爾路徑
// 注意:drawRect不能手動(dòng)調(diào)用,因?yàn)閳D形上下文我們自己創(chuàng)建不了,只能由系統(tǒng)幫我們創(chuàng)建,并且傳遞給我們
- (void)drawRect:(CGRect)rect {
// Drawing code
// 創(chuàng)建貝瑟爾路徑
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];
}
- 重寫(xiě)setProgress方法,每次調(diào)用該方法都要對(duì)View進(jìn)行重繪
- (void)setProgress:(CGFloat)progress
{
_progress = progress;
// 重新繪制圓弧
// [self drawRect:self.bounds];
// 重繪,系統(tǒng)會(huì)先創(chuàng)建與view相關(guān)聯(lián)的上下文,然后再調(diào)用drawRect
[self setNeedsDisplay];
}
畫(huà)餅圖
- 生成隨機(jī)數(shù)組的方法
- (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
// 隨機(jī)出來(lái)的臨時(shí)值等于總值,直接退出循環(huán),因?yàn)橐呀?jīng)把總數(shù)分配完畢,沒(méi)必要在分配。
[arrM addObject:@(temp)];
// 解決方式:當(dāng)隨機(jī)出來(lái)的數(shù)等于總數(shù)直接退出循環(huán)。
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;
}
- 生成隨機(jī)顏色的方法
- (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];
}
- 根據(jù)數(shù)組繪制餅狀圖的方法
- (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];
}
}
繪制柱狀圖
- 繪制柱狀圖的方法
- 主要是確定每一個(gè)柱體的高度(height)、寬度(width)、左上角橫坐標(biāo)(x)和右上角橫坐標(biāo)(y)四個(gè)元素即可
- (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];
}
}
繪制文字和圖片
- 通過(guò)drawAtPoint:withAttributes方法繪制文本
- (void)attrText
{
// 繪制文字
NSString *str = @"asfdsfsdf";
// 文字的起點(diǎn)
// Attributes:文本屬性
NSMutableDictionary *textDict = [NSMutableDictionary dictionary];
// 設(shè)置文字顏色
textDict[NSForegroundColorAttributeName] = [UIColor redColor];
// 設(shè)置文字字體
textDict[NSFontAttributeName] = [UIFont systemFontOfSize:30];
// 設(shè)置文字的空心顏色和寬度
textDict[NSStrokeWidthAttributeName] = @3;
textDict[NSStrokeColorAttributeName] = [UIColor yellowColor];
// 創(chuàng)建陰影對(duì)象
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];
}
- 通過(guò)label繪制文本
- (void)drawText
{
// 繪制文字
NSString *str = @"asfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdfasfdsfsdf";
// 不會(huì)換行
// [str drawAtPoint:CGPointZero withAttributes:nil];
[str drawInRect:self.bounds withAttributes:nil];
}
- 繪制圖片
- (void)drawRect:(CGRect)rect {
// Drawing code
// 超出裁剪區(qū)域的內(nèi)容全部裁剪掉
// 注意:裁剪必須放在繪制之前,若放在繪制之后將沒(méi)有裁剪效果
UIRectClip(CGRectMake(0, 0, 50, 50));
UIImage *image = [UIImage imageNamed:@"001"];
// 默認(rèn)繪制的內(nèi)容尺寸跟圖片尺寸一樣大
// [image drawAtPoint:CGPointZero];
// [image drawInRect:rect];
// 繪圖
[image drawAsPatternInRect:rect];
}
雪花(定時(shí)器操作)
如果在繪圖的時(shí)候需要用到定時(shí)器,通常NSTimer很少用于繪圖,因?yàn)檎{(diào)度優(yōu)先級(jí)比較低,并不會(huì)準(zhǔn)時(shí)調(diào)用,所以在展示的時(shí)候會(huì)有卡頓的現(xiàn)象
-
通常使用CADisplayLink完成繪圖過(guò)程中的定時(shí)任務(wù)操作,因?yàn)槠涿看纹聊凰⑿碌臅r(shí)候就會(huì)調(diào)用,屏幕一般一秒刷新60次
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeChange)];// 添加主運(yùn)行循環(huán) [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 調(diào)用[self setNeedsDisplay]方法并不會(huì)馬上調(diào)用drawRect方法,其實(shí)這個(gè)方法只是給當(dāng)前控件添加刷新的標(biāo)記,等下一次屏幕刷新的時(shí)候才會(huì)調(diào)用drawRect方法,其和CaDisplayLink定時(shí)器實(shí)現(xiàn)了同步,所以看不到卡頓的現(xiàn)象
圖形上下文狀態(tài)棧
- 如果以后用貝瑟爾繪制圖形[path stroke],上下文的狀態(tài)由貝瑟爾路徑?jīng)Q定
- (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的路徑轉(zhuǎn)換成CoreGraphics路徑
CGContextAddPath(ctx, path.CGPath);
// 保存一份上下文的狀態(tài)
CGContextSaveGState(ctx);
// 設(shè)置上下文狀態(tài)
CGContextSetLineWidth(ctx, 10);
[[UIColor redColor] set];
// 渲染上下文
CGContextStrokePath(ctx);
// 第二根
// 2.描述路徑
// 第一根
path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(125, 10)];
[path addLineToPoint:CGPointMake(125, 240)];
// 把路徑添加到上下文
// .CGPath 可以UIkit的路徑轉(zhuǎn)換成CoreGraphics路徑
CGContextAddPath(ctx, path.CGPath);
// 還原狀態(tài)
CGContextRestoreGState(ctx);
// // 設(shè)置上下文狀態(tài)
// CGContextSetLineWidth(ctx, 1);
//
// [[UIColor blackColor] set];
// 渲染上下文
CGContextStrokePath(ctx);
}
上下文矩陣操作
- 通過(guò)上下文矩陣操作可以將繪制好的圖形,進(jìn)行平移、放大和旋轉(zhuǎn)操作
- 注意:矩陣操作必須要在添加路徑之前,否則矩陣操作將無(wú)效
- (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);
// 旋轉(zhuǎn)
CGContextRotateCTM(ctx, M_PI_4);
// 3.把路徑添加上下文
CGContextAddPath(ctx, path.CGPath);
[[UIColor redColor] set];
// 4.渲染上下文
CGContextFillPath(ctx);
}