เมื่อเรากำหนดลักษณะของเส้นที่จะวาดเรียบร้อยแล้ว ต่อไปก็กำหนดเส้นที่จะวาด ว่าจะวาดเส้นโดยเริ่มจากจุดไหน ไปถึงจุดไหนดังนี้
for(int i = 50; i <= 300; i += 50){
CGContextMoveToPoint(context, i, 200);
CGContextAddLineToPoint(context, i, 480);
CGContextMoveToPoint(context, 0, 200 + i);
CGContextAddLineToPoint(context, 320, 200 + i);
}
เมื่อกำหนดเส้นที่จะวาดเรียบร้อยแล้ว ก็สั่งให้วาดลงไปใน context (ผืนผ้าใบ) ด้วยฟังก์ชั่น CGContextStrokePath(context) ได้เลย
CGContextStrokePath(context);
CGContextRestoreGState(context);
สำหรับความหมายของ
CGContextSaveGState(context) และ
CGContextRestoreGState(context) ให้อ่านได้จาก
บทความนี้ สั้นๆ ก็คือมันเป็นการบันทึกสถานนะของการกำหนดค่าต่างๆ ให้กับ context หลังจากนั้นเมื่อเราทำอะไรเสร็จแล้ว เราก็จะย้อนกลับไปสถานะของ context ก่อนหน้าที่เราจะทำอะไรกับมันด้วยการ restore context นั่นเอง เมื่อเราสั่งให้วาดเส้นประแล้วเราจะได้ผลลัพธ์ดังนี้
ถ้าหากเราสั่ง gradient ลงไปใน context ตอนนี้ เราจะได้ gradient เต็มหน้าจอเนื่องจาก context ของเราจะมีรูปร่างเป็นสี่เหลี่ยมผืนผ้าเต็มหน้าจอนั่นเอง แต่ที่เราต้องการจริงๆ ก็คือเราต้องการให้ gradient เฉพาะพื้นที่ใต้กราฟเท่านั้น ซึ่งเทคนิคที่เราจะใช้ก็คือ เราจะสร้าง shape ที่เป็นพื้นที่ใต้กราฟ จากนั้นเราจะ clip context ให้เป็นรูปเดียวกับ shape ที่เราวาดขึ้นมา จากนั้นจึงจะวาด gradient ลงไปใน context ที่ clip แล้ว
สมมติว่าเรามีข้อมูลอยู่ 40 ชุด โดยเราสามารถ generate ข้อมูล 40 ชุดขึ้นมาได้ดังนี้ก่อน
// generate 40 data, each limited to 150
int r = 0;
int array[40];
for(int i = 0; i <= 40; i ++){
r = arc4random() % 150;
array[i] = r;
}
เก็บ data ไว้ในตัวแปร array ขนาด 40 ต่อไปเราจะวาด Shape ที่ที่เป็นพื้นที่ใต้กราฟโดยอิงจากข้อมูล 40 ชุดที่เรามี ดังนี้
// draw graph by points stored in array
CGContextSaveGState(context);
CGContextMoveToPoint(context, 0, array[0] + graphBase);
for(int i = 0; i <= 40; i ++){
CGContextAddLineToPoint(context, i * 8, array[i] + graphBase);
}
// wrap it as a shape
CGContextAddLineToPoint(context, 320, 480);
CGContextAddLineToPoint(context, 0, 480);
CGContextAddLineToPoint(context, 0, array[0] + graphBase);
CGContextSetLineWidth(context, 3);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextStrokePath(context);
CGContextRestoreGState(context);
เหมือนกับการวาดเส้นประในตอนแรก โดยเราจะวนลูปวาดเส้นไปทีละจุดๆ ใน array เมื่อวาดครบทุกจุดแล้วเราจะวาดย้อนกลับมายังจุดเริ่มต้นใหม่อีกครั้งให้เป็น Shape ปิด และเมื่อเราทดลองวาดเส้นเพื่อตรวจสอบ Shape ที่เราวาดขึ้นมา เราจะได้ Shape หน้าตาดังนี้
Shape ถูกวาดเส้นด้วยสีแดง ต่อไปเราจะทำการ clip Context ด้วย Shape ที่เราวาดขึ้นมาโดยใช้ฟังก์ชั่น
CGContextClip(context) และเอาโค้ดในการวาดเส้นสีแดงให้ Shape ออกไป ทำให้จากโค้ดด้านบนเราจะเหลือแบบนี้
// wrap it as a shape
CGContextAddLineToPoint(context, 320, 480);
CGContextAddLineToPoint(context, 0, 480);
CGContextAddLineToPoint(context, 0, array[0] + graphBase);
CGContextSetLineWidth(context, 3);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextClip(context);
ต่อไปจะวาด Gradient ลงไปใน context ที่เหลือจากการถูก clip โดยเริ่มจากการนิยาม Gradient ก่อนว่าจะให้มีลักษณะเป็นอย่างไร
// gradient definition
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.55, 1.0};
CGColorRef endColor = [UIColor colorWithRed:0.0f
green:153.0f/256.0f
blue:249.0f/256.0f
alpha:1.0].CGColor;
CGColorRef startColor = [UIColor colorWithRed:1
green:1
blue:1
alpha:0.5].CGColor;
NSArray *colors = [NSArray arrayWithObjects:(id)startColor, (id)endColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
โค๊ด เป็นการกำหนด color space (ส่วนใหญ่แทบทั้งหมดจะใช้ RGB) กำหนดตำแหน่งของสีที่จะใช้วาด gradient กำหนดสีเริ่มต้น และสีปลายของ gradient กำหนดจุดเริ่มต้นของ gradient และจุดปลายของ gradient
วาด gradient ลงไปใน context ที่ clip ไปแล้ว และ restore state เดินกลับมาแบบนี้
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
จะได้ผลลัพธ์ดังนี้
เราได้ gradient ใต้กราฟมาแล้ว ต่อไปเราจะวาดเส้นกันล่ะ โดยจะวนลูปวาดเส้นจากข้อมูลทั้ง 40 จุดที่เรามี ดังนี้
// draw graph line
CGColorRef graphColor = [UIColor colorWithRed:0.0f
green:153.0f/256.0f
blue:249.0f/256.0f
alpha:1].CGColor;
CGContextSaveGState(context);
CGContextSetStrokeColorWithColor(context, graphColor);
CGContextSetLineWidth(context, 3);
CGContextMoveToPoint(context, 0, array[0]);
for(int i = 0; i <= 40; i ++){
CGContextAddLineToPoint(context, i * 8, array[i]);
}
CGContextStrokePath(context);
CGContextRestoreGState(context);
กำหนดสีที่จะวาด จากนั้นลูปวาดเส้นตามจุดต่างๆ ตามข้อมูลทั้ง 40 จุดที่เรามี เราก็จะได้กราฟหน้าตาแบบนี้ออกมาแล้ว
ส่วนการวาด text หรือข้อความลงไปด้วย CoreGraphic นั้น ถ้าหากเราสั่งวาดแบบปกติลงไป เราจะได้ข้อความที่กลับหัว ต้อง transform มันด้วยโดยการกำหนด text matrix ก่อนการสั่งวาด ดังนี้
// draw text
char* text ="Apple API";
CGContextSelectFont(context, "Helvetica", 16, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(context, kCGTextFill);
CGContextSetRGBFillColor(context, 1, 1, 1, 1);
CGAffineTransform xform = CGAffineTransformMake(1.0, 0.0,
0.0, -1.0,
0.0, 0.0);
CGContextSetTextMatrix(context, xform);
CGContextShowTextAtPoint(context, 120, 450, text, strlen(text));
ซอสโค้ดของตัวอย่างนี้
ดาวโหลดได้ที่นี่ หรือดาวน์โหลดจาก
GitHub ก็ได้
อ้างอิง:
An iOS 4 iPad Graphics Drawing Tutorial using Quartz 2D
Quartz 2D Programming Guide