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"];

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

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

September 21, 2011

ใส่รูปเป็น Background ให้ UINavigationBar

ในเมื่อ UINavigationBar มันทำงานไม่ถูกใจเรา ดังนั้นเราก็สร้าง Category ให้ UINavigationBar ใหม่ แบบนี้


@implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
    UIColor *color = [UIColor orangeColor];
    UIImage *img  = [UIImage imageNamed: @"nav_bar.png"];
    [img drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
    self.tintColor = color;
    [super drawRect: rect];
}

@end


override method drawInRect: ให้มันวาดรูปที่เราต้องการลงไปแทน และกำหนดสีให้ property tintColor เพื่อให้ปุ่ม back ใน Navigation bar มีสีเข้ากับรูปที่เราวาดลงไปเป็น background

ปัญหาจะตามมาอีกนิดนึงคือ title ที่อยู่บน Navigation bar อาจมีสีไม่เหมาะสมกับรูปที่เราวาดลงไปเป็น background วิธีแก้ไขก็คือเราจำเป็นต้องไปกำหนดสี และฟอนต์เอง ดังนี้


    CGRect frame = CGRectMake(0, 0, 200, 44);
    UILabel *label = [[[UILabel alloc] initWithFrame:frame] autorelease];
    
    label.backgroundColor = [UIColor clearColor];
    label.font = [UIFont systemFontOfSize:18];
    
    label.textAlignment = UITextAlignmentCenter;
    label.textColor = [UIColor blackColor];
    self.navigationItem.titleView = label;
    label.text = @"First View";


เท่านี้เราก็จะได้ UINavigationBar หน้าตาดังรูปนี้



:)

September 10, 2011

กำหนดขนาดให้ UILabel มีขนาดพอดีกับข้อความ

Developer หลายคนมักเคยเจอปัญหาในเรื่องของการกำหนดข้อความยาวๆ ให้กับ UILabel แล้วก็ไม่รู้ว่าจะต้องกำหนดขนาดให้ UILabel เป็นเท่าไหร่ดี? เนื่องจาก UILabel นั้นมันไม่ยอมขยายขนาดให้ฟิตพอดีกับปริมาณของข้อความ หรือ text ที่เราจะเอามากำหนดค่าให้มัน

จริงๆ แล้วที่เห็นปัญหาได้ชัดเจนกว่า UILabel ก็คือการกำหนดขนาดให้กับ Cell ในตารางของ UITableView ที่เรามักจะน้ำข้อมูลมาใส่ในตาราง และแต่ละ cell ของตารางก็บังเอิญมีขนาดเท่าๆ กันหมดด้วยสิ ทำให้การหาขนาดความสูงของ cell ให้มีขนาดเหมาะสมกับ content ที่จะวางลงไปในตารางจึงเป็นเรืองที่หลีกเลี่ยงแทบไม่ได้เลย

ดังนั้นเราจำเป็นต้องกำหนดขนาดให้กับ UILabel ให้พอดีกับปริมาณข้อความเอง ซึ่งวิธีหาขนาดที่เหมาะสมให้กับ UILabel สามารถหาได้ดังนี้


CGSize size = [label.text sizeWithFont: label.font 
                         constrainedToSize: CGSizeMake(300, 5000
                             lineBreakMode: UILineBreakModeWordWrap];


ถ้าโค๊ดเต็มๆ สำหรับนำไปใช้งานก็จะมีหน้าตาประมาณนี้


UILabel *label = [[UILabel alloc] init];
    label.text = @"It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).";
    

    label.numberOfLines = 0;
    label.backgroundColor = [UIColor whiteColor];
    label.font = [UIFont fontWithName:@"Helvetica"size:14];
    
    CGSize size = [label.text sizeWithFont: label.font 
                         constrainedToSize: CGSizeMake(300, 5000
                             lineBreakMode: UILineBreakModeWordWrap];
    
    label.frame = CGRectMake(10, 10, 300, size.height);
    
    [self.view addSubview:label];
    [label release];


เท่านี้เราก็จะได้ UILabel ที่มีขนาดเปลี่ยนไปตามปริมาณของข้อความแล้ว

ปล. text ตัวอย่างเอามาจากเว็บนี้

เพ่ิมฟอนต์ให้ iOS

นอกจากฟอนต์ที่มีให้ใช้อยู่แล้ว เราสามารถเพิ่ม font ที่เราต้องการลงไปได้โดยมีขั้นตอนดังนี้

  1. import ฟอนต์ที่ต้องการเข้ามาในโปรเจ็ค
  2. เพิ่มค่า property ลงในไฟล์ info.plist โดยให้เพิ่มลงใน key ที่ชื่อ UIAppFonts (ถ้าไม่มีให้สร้างใหม่) ซึ่งค่านี้จะมีชนิด value เป็น Array จากนั้นกำหนดชื่อฟอนต์ที่ต้องการ (ใส่นามสกุลด้วย เช่น .ttf)
  3. เอาไปใช้ได้เลย โอยอ้างอิงเช่นเดียวกับ iOS system font ตามปกติ


    UIFont *font = [UIFont fontWithName: @"TH Charmonman" size: 60];
    _textLabel.font = font;


หน้าตาออกมาก็จะเป็นแบบนี้



ปล. จากตัวอย่างใช้ฟอนต์ชื่อ TH Charmonman เป็น 13 ฟอนต์เทพแห่งชาติ สามารถดาวน์โหลดได้จากเว็บของ SIPA เลย หรือจะลองดูหน้าตาของฟอนต์ต่างๆ ก่อนตัดสินใจดาวน์โหลดก่อนได้ที่นี่

แถม: iOS Fonts

:)


September 06, 2011

GeekTool ลง Mac App Store แล้ว

ในที่สุด GeekTool ก็ลง Mac App Store แล้ว

แต่เดิมผมไม่ได้สนใจมันเท่าไหร่ แต่ไหนๆ มันก็ลง Mac App Store แล้วก็เลยเอามาเล่นสักหน่อย ทำแบบรีบๆ ได้ Desktop หน้าตาแบบนี้มาดูเล่นล่ะ



หลักๆ แล้วการใช้ GeekTool จะเป็นการเอารูป และ output ของการรัน command ต่างๆ มาแปะไว้บน Desktop ที่นิยมก็เช่น วันที่ เวลา ปฏิทิน การใช้เมมโมรี่ และสถานะ CPU เป็นต้น

สำหรับการนำรูปมาแปะสามารถเลือกรูปในเครื่องเราได้เลย และสามารถตั้งค่าให้มันเปลี่ยนรูปไปเรื่อยๆ ได้

ยกตัวอย่างเช่นรูปรายงานสภาพอากาศนั้นผมไปดึงรูปมาจาก Yahoo Weather โดยมีขั้นตอนดังนี้

1. ไป Yahoo Weather แล้วกำหนดสถานที่เป็น Bangkok Thailand จะได้ URL มาของสภาพอากาศมา
2. เปิด GeekTool แล้วแปะ Shell ลงไป จากนั้นตรงช่อง command ให้ใส่คำสั่งดังนี้


curl --silent "WEATHER_URL" | grep "forecast-icon" | sed "s/.*background\\:url(\\'\\(.*\\)\\')\\;\\ _background.*/\\1/" | xargs curl --silent -o /tmp/wpicture.png

ให้แทน WEATHER_URL ด้วย url ของสภาพอากาศที่ได้มา

จากคำสั่งจะเป็นการดึง HTML จาก URL ของสภาพอากาศที่ได้มาจากนั้นแกะเอาแต่ชื่อภาพของสภาพอากาศและสั่งเซฟลงไปที่ /tmp/wpicture.png

และให้กำหนดให้มัน refresh ทุกๆ เวลาเท่าไหร่ก็ได้ อาจจะสัก 1 ชั่วโมงต่อครั้ง (การสั่ง refresh ที่ว่านี้จะเป็นการกำหนดให้คำนั่งที่เรากำหนดไว้ทำงานทุกๆ เวลากี่วินาที)

3. ไปที่ GeekTool แล้วแปะ Image ลงไป ตรงค่า URL ของรูปนั้นให้ใส่เป็น file:///tmp/wpicture.png พร้อมทั้งกำหนดให้มัน refresh รูปด้วย (ประมาณ 1 ชม. ต่อครั้งก็ได้)

เท่านี้รูปสภาพอากาศก็จะเปลี่ยนแปลงไปเรื่อยๆ แล้ว


4. สำหรับอุณหภูมิและสภาพอากาศนั้นให้ใช้คำสั่งดังนี้

curl --silent "http://xml.weather.yahoo.com/forecastrss?p=THXX0002&u=c" | grep -E '(Current Conditions:|C<BR)' | sed -e 's/Current Conditions://' -e 's/<br \/>//' -e 's/<b>//' -e 's/<\/b>//' -e 's/<BR \/>//' -e 's/<description>//' -e 's/<\/description>//' -e 's/,//' | tail -n1

แทนพารามิเตอร์ p ด้วยรหัสของสถานที่ที่เราต้องการ ดูได้จาก RSS ในหน้าสภาพอากาศนั่นแหละ
เช่น หากกำหนดสภาพอากาศเป็น Bangkok แล้วกดดู RSS จะได้แบบนี้ และค่า p คือ THXX0002 นั่นเอง


ตัวอย่าง Desktop สวยๆ ที่เอา GeekTool ไปใช้:
http://desktopspotting.com/25/awesome-geektool-mac-os-x-desktop/
http://tjdyo.deviantart.com/art/My-Desktop-with-Geektool-195121469
http://www.fuelyourinterface.com/making-your-desktop-look-awesome-again/
http://www.stumbleupon.com/su/2NzrKf/smokingapples.com/software/15-geektool-desktop-inspirations-for-the-weekend
และอันนี้เซ็ตใหญ่จาก flickr ครับ http://www.flickr.com/photos/tags/geektool/interesting/


:)

September 02, 2011

ใช้ Mail.app ใน Lion แล้วส่งเมลล์ออกไม่ได้

พยายามส่ง e-mail โดยใช้โปรแกรม Mail.app ที่ติดมากับ Mac OS X Lion อยู่หลายที ไม่เคยจะส่งออกได้สักที ลอง google ดูพบว่าแทบทุกที่ให้ทำตามนี้กันหมดเลย แล้วมันจะทำให้เราส่ง e-mail ได้!

  1. Quit mail.app
  2. Go to https://www.google.com/accounts/DisplayUnlockCaptcha
  3. Unlock the captcha
  4. Launch mail
  5. Done!