天天看點

iOS 2D 繪圖(Quartz2D)

2D 繪圖

官方文檔《Quartz 2D Programming Guide》

文字漸變效果

iOS 2D 繪圖(Quartz2D)
- (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];
}
           

圓環漸變色進度條

iOS 2D 繪圖(Quartz2D)
- (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];
}
           

曲線和圓弧

iOS 2D 繪圖(Quartz2D)

圖 1

iOS 2D 繪圖(Quartz2D)

圖 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

滾動色帶進度條

iOS 2D 繪圖(Quartz2D)
- (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];
}