Friday, August 27, 2010

Easy way lazyload image on UITableViewCell

This is sample article lazyload image on UITableViewCell. The libary for use this article
http://allseeing-i.com/ASIHTTPRequest/ and the sample code from Apple
ThumbImageView.h

#import "ASIHTTPRequest.h"

@protocol ThumbImageViewDelegate;




@interface ThumbImageView : UIImageView {
id delegate;
NSString *imageName;
CGSize imageSize;
CGRect home;
BOOL dragging;
CGPoint touchLocation; // Location of touch in own coordinates (stays constant during dragging).
}

@property (nonatomic, assign) id delegate;
@property (nonatomic, retain) NSString *imageName;
@property (nonatomic, assign) CGSize imageSize;
@property (nonatomic, assign) CGRect home;
@property (nonatomic, assign) CGPoint touchLocation;

- (void)goHome;
- (void)moveByOffset:(CGPoint)offset;
- (id)initWithImageAtUrl:(NSURL*)theUrl;

@end



@protocol ThumbImageViewDelegate

@optional
- (void)thumbImageViewWasTapped:(ThumbImageView *)tiv;
- (void)thumbImageViewStartedTracking:(ThumbImageView *)tiv;
- (void)thumbImageViewMoved:(ThumbImageView *)tiv;
- (void)thumbImageViewStoppedTracking:(ThumbImageView *)tiv;

@end


ThumbImageView.m
#import "ThumbImageView.h"
#import "MoMagIO.h"

#define DRAG_THRESHOLD 10
#define ACTIVITYVIEW_TAG 123

float distanceBetweenPoints(CGPoint a, CGPoint b);

@interface ThumbImageView(PrivateMethods)

-(void)addLoadingCircle;
-(void)removeLoadingCircle;
-(void)downloadImageWithURL:(NSURL*)theUrl;

@end


@implementation ThumbImageView


@synthesize delegate;
@synthesize imageName;
@synthesize imageSize;
@synthesize home;
@synthesize touchLocation;

- (id)initWithImageAtUrl:(NSURL*)theUrl{

self = [super init];
if (self) {
[self setUserInteractionEnabled:YES];
[self setExclusiveTouch:YES];
self.image = nil;
// Add loading circle
[self addLoadingCircle];
// Asyncronous download image
[self downloadImageWithURL:theUrl];
}
return self;
}

-(void)addLoadingCircle{

UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 24, 24)];
indicatorView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
indicatorView.center = CGPointMake((152/2),(195/2));
indicatorView.tag = ACTIVITYVIEW_TAG;
[self addSubview:[indicatorView autorelease]];
[indicatorView startAnimating];
}

-(void)removeLoadingCircle{

UIActivityIndicatorView *indicatorView = (UIActivityIndicatorView*)[self viewWithTag:ACTIVITYVIEW_TAG];
[indicatorView stopAnimating];
[indicatorView removeFromSuperview];
}
-(void)downloadImageWithURL:(NSURL*)theUrl{
NSLog(@"Url for download %@",theUrl);
NSString *pathToSave = [[MoMagIO getCacheDirPath] stringByAppendingPathComponent:[[theUrl path] lastPathComponent]];
//
if(imageName != pathToSave){
[imageName release];
imageName = [pathToSave retain];
}
NSFileManager *fm = [NSFileManager defaultManager];
if([fm fileExistsAtPath:self.imageName]){
UIImage *img = [[UIImage alloc] initWithContentsOfFile:imageName];
if(img){
self.image = img;
[self setNeedsDisplay];
}
[img release];
[self removeLoadingCircle];
}else {

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:theUrl];
[request setDelegate:self];
request.didFailSelector = @selector(downloadDidFailed:);
request.didFinishSelector = @selector(downloadDidCompleted:);
request.downloadDestinationPath = imageName;
[request startAsynchronous];
}
}
-(void)downloadDidFailed:(ASIHTTPRequest *)request{
#ifdef DEBUG
NSLog(@"ThumbImageView:-(void)downloadDidFailed:(ASIHTTPRequest *)request %@",[request error]);
#endif
NSBundle *bundle = [NSBundle mainBundle];
NSString *root = [[bundle resourcePath] stringByAppendingPathComponent:@"cover_preload.jpg"];
UIImage *img = [[UIImage alloc] initWithContentsOfFile:root];
if(img){
self.image = img;
[self setNeedsDisplay];
}
[img release];
[self removeLoadingCircle];
}
-(void)downloadDidCompleted:(ASIHTTPRequest *)request{
UIImage *img = [[UIImage alloc] initWithContentsOfFile:imageName];
if(img){
self.image = img;
[self setNeedsDisplay];
}
[img release];

[self removeLoadingCircle];
}

- (id)initWithImage:(UIImage *)image {
self = [super initWithImage:image];
if (self) {
[self setUserInteractionEnabled:YES];
[self setExclusiveTouch:YES];
}
return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// store the location of the starting touch so we can decide when we've moved far enough to drag
touchLocation = [[touches anyObject] locationInView:self];
if ([delegate respondsToSelector:@selector(thumbImageViewStartedTracking:)])
[delegate thumbImageViewStartedTracking:self];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
// we want to establish a minimum distance that the touch has to move before it counts as dragging,
// so that the slight movement involved in a tap doesn't cause the frame to move.
/**CGPoint newTouchLocation = [[touches anyObject] locationInView:self];
if (dragging) {
float deltaX = newTouchLocation.x - touchLocation.x;
float deltaY = newTouchLocation.y - touchLocation.y;
[self moveByOffset:CGPointMake(deltaX, deltaY)];
}
else if (distanceBetweenPoints(touchLocation, newTouchLocation) > DRAG_THRESHOLD) {
touchLocation = newTouchLocation;
dragging = YES;
}**/
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (dragging) {
[self goHome];
dragging = NO;
} else if ([[touches anyObject] tapCount] == 1) {
if ([delegate respondsToSelector:@selector(thumbImageViewWasTapped:)])
[delegate thumbImageViewWasTapped:self];
}
if ([delegate respondsToSelector:@selector(thumbImageViewStoppedTracking:)])
[delegate thumbImageViewStoppedTracking:self];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self goHome];
dragging = NO;
if ([delegate respondsToSelector:@selector(thumbImageViewStoppedTracking:)])
[delegate thumbImageViewStoppedTracking:self];
}

- (void)goHome {
float distanceFromHome = distanceBetweenPoints([self frame].origin, [self home].origin); // distance is in pixels
float animationDuration = 0.1 + distanceFromHome * 0.001;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:animationDuration];
[self setFrame:[self home]];
[UIView commitAnimations];
}
- (void)moveByOffset:(CGPoint)offset {
CGRect frame = [self frame];
frame.origin.x += offset.x;
frame.origin.y += offset.y;
[self setFrame:frame];
if ([delegate respondsToSelector:@selector(thumbImageViewMoved:)])
[delegate thumbImageViewMoved:self];
}

@end

float distanceBetweenPoints(CGPoint a, CGPoint b) {
float deltaX = a.x - b.x;
float deltaY = a.y - b.y;
return sqrtf( (deltaX * deltaX) + (deltaY * deltaY) );
}

Then use it
// Left photo
ThumbImageView *thumbViewLeft = [[[ThumbImageView alloc] initWithImageAtUrl:url] autorelease];
thumbViewLeft.userInteractionEnabled = YES;
thumbViewLeft.exclusiveTouch = NO;
thumbViewLeft.layer.borderWidth = 1.0f;
thumbViewLeft.layer.borderColor = [[UIColor lightGrayColor] CGColor];
[thumbViewLeft setDelegate:self];
CGRect frame = [thumbViewLeft frame];
frame.origin.y = IMAGE_PADDING;
frame.origin.x = xPosition;
frame.size = CGSizeMake(imageWidth,IMAGE_HEIGHT);
[thumbViewLeft setFrame:frame];
[thumbViewLeft setHome:frame];
thumbViewLeft.tag = LEFT_IMAGE;
//image =[[UIImage alloc] initWithContentsOfFile:[root stringByAppendingPathComponent:@"cover_preload.jpg"]];
//[thumbViewLeft setImage:[ImagePopulate image:image fitInSize:thumbViewLeft.frame.size]];
//[image release];
[cell.contentView addSubview:thumbViewLeft];
xPosition += (frame.size.width + IMAGE_PADDING);


Happy with programming

Wednesday, August 18, 2010

การ embed youtube ใน UIWebView

โดยปรกติแล้วการเล่นวีดีโอผ่าน youtube ส่วนมากจะเป็น 3rd party ที่เวลาเล่นแล้ว
จำเป็นจะต้องดีดออกจาก app แต่ถ้าอยากให้เล่นใน app ก็สามารถทำได้ครับ


- (void)embedYouTube:(NSString *)urlString frame:(CGRect)frame {

NSString *embedHTML = @"starthtml starthead\ \ endhead startbody style=\"margin:0\">\
\
endbody endhtml";
NSString *html = [NSString stringWithFormat:embedHTML, urlString,
frame.size.width, frame.size.height];

if (!webView) {
webView = [[UIWebView alloc] initWithFrame:frame];
[self.view addSubview:webView];
}

[webView loadHTMLString:html baseURL:nil];

}


หลังจากนั้นแค่เรียก


[self embedYouTube:self.url frame:CGRectMake(20, 20, 280, 300)];


more information
http://apiblog.youtube.com/2009/02/youtube-apis-iphone-cool-mobile-apps.html
http://iphoneincubator.com/blog/audio-video/how-to-play-youtube-videos-within-an-application

Wednesday, August 11, 2010

UIScrollView zooming centered UIImageView

การซูมใน UIScrollView ถ้าซูมรูปเดียวคงไม่มีปัญหาอะไร เพราะมี method ให้เลือกใช้งานดังนี้

float newScale = [imageScrollView zoomScale] * maxZoomLevel;
CGRect zoomRect = [self zoomRectForScale:newScale withCenter:tapPoint];
[imageScrollView zoomToRect:zoomRect animated:YES];


แต่เปิด View ขึ้นมาแล้วให้รูปซูมขึ้นมาเลย แล้วก็ให้อยู่ center ด้วยนั้นสามารถทำได้ดังนี้ครับ

CGFloat tempx = view.center.x-160;
CGFloat tempy = view.center.y-160;
myScrollViewOffset = CGPointMake(tempx,tempy);


เเหล่งข้อมูล
http://stackoverflow.com/questions/638299/uiscrollview-with-centered-uiimageview-like-photos-app
http://stackoverflow.com/questions/1316451/center-content-of-uiscrollview-when-smaller
http://discussions.apple.com/thread.jspa?messageID=8322675

Monday, August 9, 2010

iPhone storekit - Handler cancel event by user

เมื่อเราพัฒนาเเอพพลิเคชั่นที่จำเป็นต้องใ่ช้ In-App purchase จำเป็นทำการ implement SKPaymentTransactionObserver
สำหรับการทำ Transaction ในการ purchase item ต่างๆ และเมื่อ storekit ขึ้นหน้าจอให้ confirm การ purchase จะมีสอง
ปุ่มให้เลือกคือ Cancel และ Buy จำการ cancel Transactionstate ที่ได้จะเป็น SKPaymentTransactionStateFailed ซึ่งเรา
สามารถที่จะทำการตรวจสอบว่า transaction state นั้นการจากการ fail หรือ cancel โดย user โดยเช็คได้ดังนี้

transaction.error.code != SKErrorPaymentCancelled


โ่ค้ดเเบบสมบูรณ์


- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:

[self completeTransaction:transaction];

break;

case SKPaymentTransactionStateFailed:

[self failedTransaction:transaction];

break;

case SKPaymentTransactionStateRestored:

[self restoreTransaction:transaction];

default:

break;
}
}
}

- (void) failedTransaction: (SKPaymentTransaction *)transaction
{
if (transaction.error.code != SKErrorPaymentCancelled)
{
// Optionally, display an error here.
if([self.delegate respondsToSelector:@selector(failedTransaction:)])
[self.delegate failedTransaction:transaction];
}else {
if([self.delegate respondsToSelector:@selector(canceledTransaction:)])
[self.delegate canceledTransaction:transaction];

}

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

- (void) completeTransaction: (SKPaymentTransaction *)transaction
{
if([self.delegate respondsToSelector:@selector(completeTransaction:)])
[self.delegate completeTransaction:transaction];

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

- (void) restoreTransaction: (SKPaymentTransaction *)transaction
{
if([self.delegate respondsToSelector:@selector(restoreTransaction:)])
[self.delegate restoreTransaction:transaction];

[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}