2D 繪圖
官方文檔《Quartz 2D Programming Guide》
文字漸變效果
- (void)viewDidLoad {
UILabel *text = [[UILabel alloc] initWithFrame:CGRectMake(, , , )];
text.text = @"Juphoon 菊風測試";
[text sizeToFit];
// 必須要把Label添加到view上,如果不添加到view上,label的圖層就不會調用drawRect方法繪制文字,也就沒有文字裁剪了。
[self.view addSubview:text];
_colorLayer = [CAGradientLayer layer];
_colorLayer.frame = text.frame;
_colorLayer.colors = @[(id)[self radomColor].CGColor, (id)[self radomColor].CGColor, (id)[self radomColor].CGColor];
_colorLayer.startPoint = CGPointMake(, );
_colorLayer.endPoint = CGPointMake(, );
[self.view.layer addSublayer:_colorLayer];
// mask層工作原理:按照透明度裁剪,隻保留非透明部分,文字就是非透明的,是以除了文字,其他都被裁剪掉,這樣就隻會顯示文字下面漸變層的内容,相當于留了文字的區域,讓漸變層去填充文字的顔色。
// 設定漸變層的裁剪層
_colorLayer.mask = text.layer;
// 注意:一旦把label層設定為mask層,label層就不能顯示了,會直接從父層中移除,然後作為漸變層的mask層,且label層的父層會指向漸變層,這樣做的目的:以漸變層為坐标系,友善計算裁剪區域,如果以其他層為坐标系,還需要做點的轉換,需要把别的坐标系上的點,轉換成自己坐标系上點,判斷目前點在不在裁剪範圍内,比較麻煩。
// 父層改了,坐标系也就改了,需要重新設定label的位置,才能正确的設定裁剪區域。
text.frame = _colorLayer.bounds;
[NSTimer scheduledTimerWithTimeInterval: target:self selector:@selector(changeColor) userInfo:nil repeats:YES];
}
- (UIColor *)radomColor {
CGFloat r = arc4random_uniform() / ;
CGFloat g = arc4random_uniform() / ;
CGFloat b = arc4random_uniform() / ;
return [UIColor colorWithRed:r green:g blue:b alpha:];
}
- (void)changeColor {
_colorLayer.colors = @[(id)[self radomColor].CGColor,
(id)[self radomColor].CGColor,
(id)[self radomColor].CGColor,
(id)[self radomColor].CGColor,
(id)[self radomColor].CGColor];
}
圓環漸變色進度條
- (void)viewDidLoad {
CALayer *background = [CALayer layer];
background.frame = CGRectMake(, , , );
background.backgroundColor = [UIColor yellowColor].CGColor;
[self.view.layer addSublayer:background];
UIBezierPath *path = [[UIBezierPath alloc] init];
[path addArcWithCenter:CGPointMake(, ) radius: startAngle:M_PI endAngle:( * M_PI) clockwise:YES];
_progress = [CAShapeLayer layer];
_progress.lineWidth = ;
_progress.strokeColor = [UIColor purpleColor].CGColor;
_progress.fillColor = [UIColor clearColor].CGColor;
_progress.path = path.CGPath;
_progress.strokeStart = f;
_progress.strokeEnd = f;
CAGradientLayer *colorLayer = [CAGradientLayer layer];
colorLayer.colors = @[(id)[UIColor redColor].CGColor, (id)[UIColor orangeColor].CGColor, (id)[UIColor blueColor].CGColor];
colorLayer.startPoint = CGPointMake(, );
colorLayer.endPoint = CGPointMake(, );
colorLayer.mask = _progress;
colorLayer.frame = background.bounds;
[background addSublayer:colorLayer];
[NSTimer scheduledTimerWithTimeInterval: target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];
}
- (void)updateProgress {
static CGFloat progress = f;
progress += ;
BOOL endTransaction = NO;
if (progress >= ) {
progress = f;
endTransaction = YES;
}
BOOL backup = [CATransaction disableActions];
[CATransaction setDisableActions:endTransaction];
_progress.strokeEnd = progress;
[CATransaction setDisableActions:backup];
}
曲線和圓弧
圖 1
圖 2
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextMoveToPoint(context, , );
CGContextAddLineToPoint(context, , );
CGContextSetLineWidth(context, );
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextStrokePath(context);
CGContextRestoreGState(context);
CGContextSaveGState(context);
CGContextTranslateCTM(context, , );
CGContextSetLineWidth(context, );
CGContextAddEllipseInRect(context, CGRectMake(, , , ));
CGContextSetStrokeColorWithColor(context, [UIColor yellowColor].CGColor);
CGFloat lengths[] = {, , , };
CGContextSetLineDash(context, , lengths, );
CGContextStrokePath(context);
CGContextRestoreGState(context);
CGContextSaveGState(context);
CGContextTranslateCTM(context, , );
CGContextAddEllipseInRect(context, CGRectMake(, , , ));
CGContextMoveToPoint(context, , );
CGContextAddLineToPoint(context, , );
CGContextAddLineToPoint(context, , );
CGContextSetLineWidth(context, );
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextMoveToPoint(context, , );
CGContextAddArcToPoint(context, , , , , );
CGContextAddEllipseInRect(context, CGRectMake(, , , ));
CGContextAddEllipseInRect(context, CGRectMake(, , , ));
CGContextStrokePath(context);
[@"A" drawInRect:CGRectMake(, , , ) withAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:], NSForegroundColorAttributeName : [UIColor whiteColor]}];
[@"B" drawInRect:CGRectMake(, , , ) withAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:], NSForegroundColorAttributeName : [UIColor whiteColor]}];
[@"C" drawInRect:CGRectMake(, , , ) withAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:], NSForegroundColorAttributeName : [UIColor whiteColor]}];
CGContextRestoreGState(context);
-
CGContextSetLineDash
lengths的值{5, 5, 10,5}表示先繪制5個點,再跳過5個點,再繪制10個點,再跳過5個點,如此反複
lengths的值{10,5},表示先繪制10個點,再跳過5個點,如此反複
phase值為5,lengths的值{10,5},表示首先繪制【10減去5】,再跳過5,繪制10,反複繪制(繪制第一個點的時候偏移phase)
-
CGContextAddArcToPoint
效果見圖 2
計算弧線過程是這樣的,首先要明白current point的概念,在你調用CGContextMoveToPoint時,你其實是将一個點設定為current point,或者你調用了CGContextAddLineToPoint也同樣在繪制線段後将current point修改為添加線段後的坐标點。
在調用了CGContextAddArcToPoint這個方法的時候 ,系統在這個時候就計算出來了2個直線了,分别為current point (下面叫做p1)與 start point(下面叫p2)連接配接的直線和start point與end point(下面叫p3)連接配接的直線。由于我們是從p1連接配接到p2,向量方向是p1->p2的,再加上p2到p3,向量方向是p2->p3,這樣實際上我們就知道了圓弧所在的大概方向,他是處于兩條直線夾角那一邊!
然後呢我們需要明白CGContextAddArcToPoint是用來畫弧線的,但是這個弧線是有特點的,就是它會和我們之前計算出來的2個直線相切的,那麼理論上不管半徑有多小多大,隻要之前兩條直線的夾角大于0度小于180度,也就是說不是2條直線不是在一條直線上就好,我們總能找到一個這樣的圓弧,使圓弧所在的圓和兩條直線相切。
這個時候又出現神奇的地方了,就是當計算出這段弧度的時候,我們的p1點會和圓弧的起點自動繪制一條線段,然後再繼續繪制弧線,最後end point落在弧線結束的地方。
這個說明了什麼,說明實際上我們傳入CGContextAddArcToPoint的兩個點并不一定在我們繪制的圖形裡出現,他們隻不過用來計算直線,定位圓弧用的,圓弧出來後,他的起點被連接配接到調用CGContextAddArcToPoint前的current point上,而圓弧的終點被設定為新的current point
滾動色帶進度條
- (void)viewDidLoad {
// Use a horizontal gradient
CAGradientLayer *layer = (id)[self layer];
[layer setStartPoint:CGPointMake(, )];
[layer setEndPoint:CGPointMake(, )];
// Create colors using hues in +5 increments
NSMutableArray *colors = [NSMutableArray array];
for (NSInteger hue = ; hue <= ; hue += ) {
UIColor *color = [UIColor colorWithHue: * hue / saturation: brightness: alpha:];
[colors addObject:(id)[color CGColor]];
}
[layer setColors:[NSArray arrayWithArray:colors]];
}
- (void)performAnimation {
// Move the last color in the array to the front
// shifting all the other colors.
CAGradientLayer *layer = (id)[self layer];
NSMutableArray *mutable = [[layer colors] mutableCopy];
id lastColor = [mutable lastObject];
[mutable removeLastObject];
[mutable insertObject:lastColor atIndex:];
NSArray *shiftedColors = [NSArray arrayWithArray:mutable];
// Update the colors on the model layer
[layer setColors:shiftedColors];
// Create an animation to slowly move the gradient left to right.
CABasicAnimation *animation;
animation = [CABasicAnimation animationWithKeyPath:@"colors"];
[animation setToValue:shiftedColors];
[animation setDuration:];
[animation setRemovedOnCompletion:YES];
[animation setFillMode:kCAFillModeForwards];
[animation setDelegate:self];
[layer addAnimation:animation forKey:@"animateGradient"];
}
- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)flag {
[self performAnimation];
}