2011年7月6日 星期三

checksum function

#import <CommonCrypto/CommonDigest.h>


- (NSString *)md5StringFromData:(NSData *)data
{
    void *cData = malloc([data length]);
    unsigned char resultCString[16];
    [data getBytes:cData length:[data length]];
    
    CC_MD5(cData, [data length], resultCString);
    free(cData);
    
    NSString *result = [NSString stringWithFormat:
                        @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
                        resultCString[0], resultCString[1], resultCString[2], resultCString[3], 
                        resultCString[4], resultCString[5], resultCString[6], resultCString[7],
                        resultCString[8], resultCString[9], resultCString[10], resultCString[11],
                        resultCString[12], resultCString[13], resultCString[14], resultCString[15]
                        ];
    return result;
}

reference from :http://stackoverflow.com/questions/1028742/compute-a-checksum-on-the-iphone-from-nsdata

2011年6月26日 星期日

Detect the version of app

NSString* version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];

2011年6月19日 星期日

get rgb data from UIImage

xxx.h

typedef unsigned char byte;

typedef struct RGBPixel    
{
    byte    red;
    byte    green;
    byte    blue;
}   RGBPixel;

typedef struct RGBAPixel    
{
    byte    red;
    byte    green;
    byte    blue;
    byte    alpha;    
}   RGBAPixel;

@interface UIImage(XXX)
- (RGBPixel*)bitmapFromBMP;
- (RGBAPixel*)bitmapFromPNG;
@end

xxx.m

#define RGBA                4
#define RGBA_8_BIT          8

@implementation UIImage(XXX)

- (RGBPixel*)bitmapFromBMP
{
    CFDataRef bitmapData = CGDataProviderCopyData(CGImageGetDataProvider(self.CGImage)); 
    UInt8* pixelByteData = malloc(CFDataGetLength(bitmapData));
    if (!pixelByteData)
        return nil;
    
    CFDataGetBytes(bitmapData, CFRangeMake(0, CFDataGetLength(bitmapData)), pixelByteData);
    return (RGBPixel*)pixelByteData;
}

- (RGBAPixel*)bitmapFromPNG
{
    size_t bytesPerRow = self.size.width * RGBA;
    size_t byteCount = bytesPerRow * self.size.height;
    CGContextRef context = 0;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    UInt8* pixelByteData = malloc(byteCount);
    RGBAPixel* pixelData = nil;
    
    if (!colorSpace) 
    {
        return nil;
    }
    
    if (!pixelByteData)  
    {
        CGColorSpaceRelease( colorSpace );
        return nil;
    }
    context = CGBitmapContextCreate((void*) pixelByteData,
                                    self.size.width,
                                    self.size.height,
                                    RGBA_8_BIT,
                                    bytesPerRow,
                                    colorSpace,
                                    kCGImageAlphaPremultipliedLast);
    
    if (!context)
    {
        free( pixelByteData );
        CGColorSpaceRelease( colorSpace );
        return nil;
    }
    CGRect rect = { { 0 , 0 }, { self.size.width, self.size.height }    };    
    CGContextDrawImage( context, rect, self.CGImage );
    pixelData = (RGBAPixel*) CGBitmapContextGetData(context);
    
    return pixelData;
    
}

@end

在 iPhone 上實現小畫家基本功能

.h

UIImageView *drawImage_;
CGPoint lastPoint_;

.m
-(id) init
{
if( (self=[super init])) {
        drawImage_ = [[[UIImageView alloc]initWithImage:nil]autorelease];
        drawImage_.frame = CGRectMake(0, 50, 320,400);
        [[[CCDirector sharedDirector]openGLView] addSubview:drawImage_];
        self.isTouchEnabled = YES;
}
return self;
}

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    lastPoint_ = [touch locationInView:drawImage_];    
}

- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint currentPoint = [touch locationInView:drawImage_];
    UIGraphicsBeginImageContext(drawImage_.frame.size);
    
    [drawImage_.image drawInRect:CGRectMake(0, 0, drawImage_.frame.size.width, drawImage_.frame.size.height)];
    CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound); 
    CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5.0);
    CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 1.0,0.0,0.0, 1.0);
    CGContextBeginPath(UIGraphicsGetCurrentContext());
    CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint_.x, lastPoint_.y);
    CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
    CGContextStrokePath(UIGraphicsGetCurrentContext());
    drawImage_.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    lastPoint_ = currentPoint;
}




2011年6月17日 星期五

Detect the orientation of iPhone view

step 1: add a notification

    [[NSNotificationCenter defaultCenter] addObserver:self 
                         selector:@selector(receivedRotate:)                                          
                             name:UIDeviceOrientationDidChangeNotification 
                           object:nil];



step 2: remove a notification

    [[NSNotificationCenter defaultCenter] removeObserver:self  
                             name:UIDeviceOrientationDidChangeNotification 
                           object:nil];


step 3: implement "receiveRotate"

- (void)receivedRotate:(NSNotification*)notification
{
    UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
    if (deviceOrientation == UIDeviceOrientationPortrait)
    {
        ...
    }
}

Transition between portrait and landscape

Portrait View :
WhatsOnLandscapeController *whatsOnLandscape = [[[WhatsOnLandscapeController alloc] initWithNibName:@"WhatsOnLandscape" bundle:nil]autorelease];

[whatsOnLandscape setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentModalViewController:whatsOnLandscape animated:YES];

Landscape View :
[Leave Landscape View]
[self dismissModalViewControllerAnimated:YES];

2011年6月15日 星期三

List iPhone Country Code

NSLocale *currentLocale = [NSLocale currentLocale];

NSArray *countryArray = [NSLocale ISOCountryCodes];

for (NSString *key in countryArray)
{
NSLog(@"code : %@\n",key);
NSString *displayNameString = [currentLocale displayNameForKey:NSLocaleCountryCode value:key];
NSLog(@"displayNameString : %@\n",displayNameString);
}


NSLog(@"Country Code is %@", [currentLocale objectForKey:NSLocaleCountryCode]);

To shutdown multitasking for app

If you do not want your application to remain in the background when it is quit, you can explicitly opt out of the background execution model by adding the UIApplicationExitsOnSuspend key to your application’s Info.plist file and setting its value to YES

2011年6月13日 星期一

Xcode4 Build Error - "couldn't add 'com.apple.XcodeGenerated' ta"

warning: couldn't add 'com.apple.XcodeGenerated' tag to '/Users/ErawppaArtist/Library/Developer/Xcode/DerivedData/Gem-fazbkuxrbdcggzdfnxewcqymqaey/Build/Intermediates/Gem.build': Error Domain=NSPOSIXErrorDomain Code=2 UserInfo=0x20212f0a0 "The operation couldn’t be completed. No such file or directory"

Solution
sudo chmod -R 777 /Users/XXX/Library/Developer/

2011年6月10日 星期五

Customize NavigationBarItem

    UIViewController *mine = [self.navigationController.viewControllers objectAtIndex:0];
    
    UIImage *nextImg = [UIImage imageNamed:@"NextIcon.png"];
    UIButton *nextBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    [nextBtn setBackgroundImage:nextImg forState:UIControlStateNormal];
    nextBtn.frame = CGRectMake(0, 0, nextImg.size.width, nextImg.size.height);
    [nextBtn addTarget:self action:@selector(nextAction:) forControlEvents:UIControlEventTouchUpInside];
    
    UIBarButtonItem *next = [[[UIBarButtonItem alloc] initWithCustomView:nextBtn]autorelease];
    
    mine.navigationItem.rightBarButtonItem = next;

利用 NSDate 抓取今日時間

- (NSString *)todayDate
{
    NSDate *today = [NSDate date];    
    NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]autorelease];
    NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
    NSDateComponents *components = [gregorian components:unitFlags fromDate:today];
    return [NSString stringWithFormat:@"%.4d-%.2d-%.2d",[components year],[components month],[components day]];
}

- (NSString *)currTime
{
    NSDate *today = [NSDate date];    
    NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]autorelease];
    NSUInteger unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit
    NSDateComponents *components = [gregorian components:unitFlags fromDate:today];
    return [NSString stringWithFormat:@"%.2d:%.2d",[components hour],[components minute]];
}

2011年6月1日 星期三

iPhone + Base64

Download Base64 and add it into your project.

#import "NSData+Base64.h"


{
    NSString *str =@"tix.lo";
    
    NSData *strData = [str dataUsingEncoding:NSUTF8StringEncoding];
    
    NSString *encodedStr = [strData base64EncodedString];
    
    NSLog(@"Base64 Encoded: %@",encodedStr);
    
    //Base64 Decoding
    NSData *decodedData = [NSData dataFromBase64String:encodedStr];
    
    NSString *decodedStr = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];
    
    NSLog(@"Base64 Decoded: %@",decodedStr );

}

source from : reference

2011年5月30日 星期一

NSMutableDictionary & NSKeyedArchiver

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"raw"];
    
    NSMutableDictionary *data = [[[NSMutableDictionary alloc]init]autorelease];
    [data setObject:[NSString stringWithFormat:@"aaa"] forKey:@"aaaKey"];
    [data setObject:[NSString stringWithFormat:@"bbb"] forKey:@"bbbKey"];
    [NSKeyedArchiver archiveRootObject:data toFile:path];
    
    NSMutableDictionary *data = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    NSLog(@"aaaKey : %@\n",[data objectForKey:@"aaaKey"]);
    NSLog(@"bbbKey : %@\n",[data objectForKey:@"bbbKey"]);

NSUserDefaults

[[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:@"111"] forKey:@"myDefaultKey"];

pList 的存放路徑為
/Users/<User>/Library/Application Support/iPhone Simulator/<XCode Version>/Applications/<Application ID>/Library/Preferences/

2011年5月28日 星期六

NSKeyedArchiver sample

NSArray *myArray1 = ....;
NSArray *myArray2 = ....;
NSData *buffer = [NSKeyedArchiver archivedDataWithRootObject: myArray1];
myArray2 = [NSKeyedUnarchiver unarchiveObjectWithData: buffer];

2011年5月25日 星期三

Customize Application Name

1. Add a strings file in your project and the file's name is "InfoPlist.strings"
2. Add a property into info.plist, "Application has localized display name"
3. Edit the Info Page of "InfoPlist.strings" and add multi-language
4. Add string in different language file.
    ex: "English" file -> "CFBundleDisplayName"="MyApp";
    ex: "zh_TW" file -> "CFBundleDisplayName"="我的應用程式";
    ex: "zh_CN" file -> "CFBundleDisplayName"="...";
  ...

2011年5月23日 星期一

How to post message to Facebook from iPhone App

Step 1: Create a new application of facebook from here

Step 2: Download Facebook SDK
Step 3: Add Facebook SDK into your project
Step 4: Replace the Api Key Values in FBControl.m


static NSString* kApiKey = @"578b8cdff0fce3aad6b249162bca2e15";
static NSString* kApiSecret = @"09c0f53fa229b45318099aa728b496ef";

Step 5: Replace the definition of strings
#define POST_NAME               @"iOSApp test name message!"
#define POST_WEBSITE            @"http://www.erawppa.com/"
#define POST_CAPTION            @"iOSApp caption message!"
#define POST_PRODUCT_ICON       @"http://www.erawppa.com/notymoky/images/p5.jpg"
#define POST_PRODUCT_WEBSITE    @"http://www.erawppa.com/notymoky"
#define POST_MSGPROMPT_FMT      @"enter your message"

Step 6: Using Facebook SDK
    FBControl *fbControl_ = [[FBControl alloc]init];
    [fbControl_ postToFacebook:@"98765443"];

How to post message to Twitter from iPhone App

Step 1: Create a new application of Twitter from app registration and you will see the information as below.
It contains two key values we will need.

Consumer key

uc5Ag3Oog5Ot3lWtt4w91Q

Consumer secret

ZFcLlWfQEq5poFDpFuTRHAVdHWPVctQYu6wXM2qpTQ
Step 2: Download Twitter SDK
Step 3: Add Twitter SDK into your project.
Step 4: Add libxml2.dylib into Framework
Step 5: Add $(SDKROOT)/usr/include/libxml2 in "Header Search Paths"
Step 6: Replace key values with previous what we got in TwitterControl.m
#define kOAuthConsumerKey @"uc5Ag3Oog5Ot3lWtt4w91Q"
#define kOAuthConsumerSecret @"ZFcLlWfQEq5poFDpFuTRHAVdHWPVctQYu6wXM2qpTQ"

Step 7: Integrate Twitter SDK with your project
  • Add #import "TwitterControl.h" and TwitterControlProtocol in xxxx.h
  • Implement TwitterControlProtocol
-(void)didPostComplete:(TwitterControl *)control success:(BOOL)result
{
    [control autorelease];
}
  • Post message to twitter
[TwitterControl postMessage:self msg:@"your message"];


Reference from here

Memory Management - 1

  Objective-C 支援 Garbage Collection, 但 iPhone 並不支援 Garbage Collection, 所以 iPhone 開發者在對於沒有使用到的 Object 必須要自己釋放.

iPhone 使用到 Reference Counting 的機制來幫助開發者管理記憶體, 何謂 Reference Counting 呢 ? 簡單的說就是對於每個 Object 都有個 counter, 只要此 Object 被 alloc 內部的 counter 就會累計加 1, 而 Object 被 release 時, 內部 counter 就會減 1, 當 counter 被遞減成 0 時, 此 Object 就會被 OS 釋放.

iPhone 基本操作中會遞增 counter 的有 alloc, new, retain, 而會遞減 counter 則是 release.


NSObject *obj = [[NSObject alloc]init];
...
NSObject *obj = [NSObject new];
...
[obj retain];
...
[obj release];
...


一般來說, 當開發者配置 memory 之後, 就一定要釋放, 不然就會產生 memory leakage. 在 iPhone Programming 中, 針對 memory 的寫法, 大概有幾個簡單的原則, 只要遵循這些原則, 基本上就可以大幅降低 memory leakage 的問題.
1. 誰遞增 reference counter, 就由誰負責遞減 reference counter
    針對這點, 這邊舉了兩個例子來分析這彼此的差異
case 1:


Modude A:
- (NSString *)generateString
{
    myString = [[NSString alloc]initWithString:@"test"];
    return myString;
}

Module B:
-(void)function
{
    NSString *string = [ModuleA generateString];
    ...
    [string release];
}



case 2:


Modude A:
- (NSString *)generateString
{
    myString = [[NSString alloc]initWithString:@"test"];
    return myString;
}

- (void)releaseString:(NSString *)string
{
    [string release];
}

Module B:
-(void)function
{
    NSString *string = [ModuleA generateString];
    ...
    [ModuleA releaseString:string];
}


單看上述這兩個例子, 或許你可能會覺得兩個都沒錯, 記憶體的確也沒有遺漏, 實際上也是如此,兩個方式語法上都沒有錯誤, 但是一般不管是在寫遊戲還是應用程式, 期複雜度一定遠遠超過上面的例子, 當專案越大時, 在 case 1 的情況下, 要去檢查 [ModuleA generateString在使用上是否有遺漏 release 的情況, 這將會是一個麻煩的事情. 相反的, 若是使用 case 2 的情況下, 要做相同檢查的動作, 只要在 Module A 中做簡單的修改即可得知是否有少 call releaseString. 在 Module A 中簡單的增加一個 allocCounter 變數, 最終只要 allocCounter > 0, 就代表有 memory 的洩露.

Modude A:
- (NSString *)generateString
{
    myString = [[NSString alloc]initWithString:@"test"];
    allocCounter++;
    return myString;
}

- (void)releaseString:(NSString *)string
{
    [string release];
    allocCounter--;
}


2. 善用 autorelease
每個 iPhone 應用程式都會有一個基本的  Autorelease Pool, 也就是在  main.m 中所建立的

int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
int retVal = UIApplicationMain(argc, argv, nil, @"XXXDelegate");
[pool release];
return retVal;
}


我們可以透過這個 Autorelease Pool 的協助, 來幫助我們更容易的管理記憶體, 使用方式很簡單, 只要在 object 之後呼叫 autorelease 即可.

NSString *myString = [[[NSString alloc]initWithString:@"test"]autorelease];


上述的 NSString 配置方式和先前的最大不同在於, myString 經過呼叫 autorelease 之後, 就不需要在自己呼叫 release 去做釋放的動作, 釋放 myString 會由 iOS 自動幫開發者執行. 那究竟 iOS 何時會去做這件事情呢 ? 簡單的說, 當 iOS 完成了一個 event loop 時, 就會去檢查 Autorelease Pool 是否有需要"自動遞減" counter 的物件, 然後會將 counter 變為  0 的物件釋放. 我們可以做一個簡單的測試, 在 iPhone 中開啓一新專案, 並透過 UIButton 的點擊去呼叫下面測試函式. 使用 UIButton 主要目的是為了讓兩次的呼叫是在不同的 event loop 中執行.

- (void)testAutorelease

{
    static NSObject *obj = nil;
    if (!obj)
    {
        obj = [[[NSObject alloc]init]autorelease];
        [obj retain];
    }
    NSLog(@"obj counter : %d\n",[obj retainCount]);
}



在連續兩次叫用 testAutorelease 時所出現的結果, 第一次顯示的 counter 值是 2, 而第二次顯示的 counter 是 1. 這結果很明顯的告知我們, 當 event loop 結束時, iOS 會針對 autorelease pool 裡的物件自動去做 counter 遞減動作.

由這結果可以得知, 若能善用 autorelease 則可以大幅降低維護記憶體的複雜度. 但是有一個需要額外注意的是, 因為 autorelease 的是放時間點是在 event loop 執行完成之後, 在這過程中是不會有釋放的動作, 所以若太大量的物件都交給 Autorelease Pool 管理, 很有可能會在執行其間遇到記憶體不足的問題, 所以要如何利用 autorelease 是每一個 iPhone 開發者都要思考的.


Retrieve Scores from Leaderboard

GKLeaderboard* leaderboard = nil;
if ([players count] > 0)
{
    leaderboard = [[[GKLeaderboard alloc] initWithPlayerIDs:players] autorelease];
}
else
{
    leaderboard = [[[GKLeaderboard alloc] init] autorelease];
    leaderboard.playerScope = playerScope;
}

if (leaderboard != nil)
{
    leaderboard.timeScope = timeScope;
    leaderboard.category = category;
    leaderboard.range = range;
    [leaderboard loadScoresWithCompletionHandler:^(NSArray* scores, NSError* error)
    {
        NSMutableArray *retrievePlayerIDs = [[[NSMutableArray alloc]init]autorelease];
    
        for (GKScore *s in scores)
        {
            [retrievePlayerIDs addObject:s.playerID];        
        }
    
        [GKPlayer loadPlayersForIdentifiers:retrievePlayerIDs withCompletionHandler:^(NSArray *playerArray, NSError *error)
         {
             NSMutableArray *retrievePlayerAlias = [[[NSMutableArray alloc]init]autorelease];
             for (GKPlayer* player in playerArray)
             {
                 NSLog(@"Player : %@",player.alias);

             }
         }]; 
    }];

}