September 24, 2011

สั่งให้ UIView เคลื่อนที่แนวโค้ง

การสั่งให้ UIView ทั้งหลายแหล่เคลื่อนที่เป็นแนวเส้นตรงนั้นไม่ยาก เพียงแต่กำหนดจุดเริ่มต้น กับจุดปลายผ่านบล็อค beginAnimations:context: และ commitAnimations เท่านั้น

สำหรับการเคลื่อนที่ของ UIView เป็นเส้นโค้งนั้น หลักการก็คือเราจะกำหนดเส้นทางการเคลื่อนที่ให้มัน จากนั้นกำหนด animation ให้กับ layer ของ UIView ที่ต้องการให้เคลื่อนที่

ค้นหาเส้นทางการเคลื่อนที่
เราจะกำหนดเส้นทางการเคลื่อนที่ แบบเดียวกับการวาดเส้นโค้งอย่าง Bézier curve ซึ่งเจ้า Bézier curve นี้ก็จะประกอบด้วยจุดเริ่ม จุดปลาย และจุดควบคุมหรือ control point (อ่านรายละเอียดเกี่ยวกับลักษณะของ Bézier curve ที่นี่)

ผมจะวาดเส้นโค้งขึ้น จากขอบทางซ้าย ไปทางขวาของจอ iPad ผมสามารถกำหนดเส้นโค้งได้ประมาณนี้

CGMutablePathRef curvedPath = CGPathCreateMutable();
CGPathMoveToPoint(curvedPath, NULL, 0, 512);
CGPathAddQuadCurveToPoint(curvedPath, NULL, 768/2, 0, 768, 512);

ซึ่งขนาดความโค้งว่าจะโค้งมากหรือน้อย โค้งขึ้นหรือโค้งลง สามารถกำหนดได้ที่จุดควบคุม จากโค๊ดด้านบนลองวาดออกมาเป็นเส้นดูจะได้เส้นหน้าตาแบบนี้



ส่วนวิธีการวาดนั้น สามารถทำได้ดังนี้

//Create a bitmap graphics context, you will later get a UIImage from this
UIGraphicsBeginImageContext(CGSizeMake(768,1024));
CGContextRef ctx = UIGraphicsGetCurrentContext();

//Set variables in the context for drawing
CGContextSetLineWidth(ctx, 1.5);
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);

CGContextMoveToPoint(ctx, 0, 512);
CGContextAddQuadCurveToPoint(ctx, 768/2, 0, 768, 512);

//Draw the line
CGContextDrawPath(ctx, kCGPathStroke);

//Get a UIImage from the current bitmap context we created at the start and then end the image context
UIImage *curve = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

//With the image, we need a UIImageView
UIImageView *curveView = [[UIImageView alloc] initWithImage:curve];
//Set the frame of the view - which is used to position it when we add it to our current UIView
curveView.frame = CGRectMake(0, 0, 768, 1024);
curveView.backgroundColor = [UIColor clearColor];

[self.view addSubview:curveView];

เมื่อดูจากเส้นที่วาดออกมาแล้ว พบว่าเป็นเส้นทางการเคลื่อนที่ในแนวโค้งที่เราต้องการ เราก็เอาจุดที่นิยาม Bézier curve นี้แหละมาใช้งาน

กำหนดเส้นทางการเคลื่อนที่
เราจะใช้ CAKeyframeAnimation ในการกำหนดเส้นทางการเคลื่อนที่ โดยการกำหนดค่าต่างๆ ให้กับ CAKeyframeAnimation ดังนี้

//Prepare the animation - we use keyframe animation for animations of this complexity
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.fillMode = kCAFillModeForwards;
pathAnimation.removedOnCompletion = NO;
pathAnimation.duration = 5.0;
//Lets loop continuously for the demonstration
pathAnimation.repeatCount = 1000;

จากโค๊ดจะบอกว่าให้เล่น animation นี้ 5 นาทีต่อ 1 รอบ กำหนดให้เล่น 1000 รอบ สำหรับรายละเอียดของ property ตัวอื่นๆ ที่ไม่ได้กล่าวถึงแต่ละตัวสามารถอ่านได้จากที่นี่เลย

ต่อไปก็กำหนดเส้นทางเดินให้ animation ตัวนี้ล่ะ โดยใช้เส้นที่เราได้เลือกแล้วดังกล่าวไปข้างต้น ดังนี้

CGMutablePathRef curvedPath = CGPathCreateMutable();
CGPathMoveToPoint(curvedPath, NULL, 0, 512);
CGPathAddQuadCurveToPoint(curvedPath, NULL, 768/2, 0, 768, 512);

pathAnimation.path = curvedPath;
CGPathRelease(curvedPath);

เมื่อ animation และเส้นทางการเคลื่อนที่พร้อมแล้ว เราก็กำหนดเส้น animation นี้ให้กับ UIView ที่ต้องการได้เลย ดังนี้

[myView.layer addAnimation:pathAnimation forKey:@"position"];

เราก็จะได้การเคลื่อนที่เป็นเส้นโค้งจากจุดต้นของเส้นทางซ้ายสุด ไปจุดปลายของเส้นทางขวาสุดสมใจ

แอบเอาโค๊ดมาจากที่นี่