2013年12月27日 星期五

在yourApp-Prefix.pch中加入你要引用的.h檔,即可以自動引用到每一個Class中

#import <Availability.h>

#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif

#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>

#include "Define.h"    //<===加在這
#include "KSMacros.h"  //<===加在這
#include "Singleton.h" //<===加在這


#endif

2013年12月25日 星期三

可以在關閉App後仍能保存物件的NSUserDefaults


//儲存到NSUserDefaults 
-(void)saveNSUserDefaults 
NSString *myString = @"enuola"; 
int myInteger = 100; 
float myFloat = 50.0f; 
double myDouble = 20.0; 
NSDate *myDate = [NSDate date]; 
NSArray *myArray = [NSArray arrayWithObjects:@"hello", @"world", nil]; 
NSDictionary *myDictionary = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:@"enuo", @"20", nil] forKeys:[NSArray arrayWithObjects:@"name", @"age", nil]]; 

//將上面的物件儲存到NSUserDefaults中 
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 
//除NSNumber需用對應的型別以外,其他的都是使用setObject:forKey: 
[userDefaults setInteger:myInteger forKey:@"myInteger"]; 
[userDefaults setFloat:myFloat forKey:@"myFloat"]; 
[userDefaults setDouble:myDouble forKey:@"myDouble"]; 

[userDefaults setObject:myString forKey:@"myString"]; 
[userDefaults setObject:myDate forKey:@"myDate"]; 
[userDefaults setObject:myArray forKey:@"myArray"]; 
[userDefaults setObject:myDictionary forKey:@"myDictionary"]; 

//建議同步,但是不是必須的 
[userDefaults synchronize]; 


//從NSUserDefaults讀取資料 
-(void)readNSUserDefaults 
NSUserDefaults *userDefaultes = [NSUserDefaults standardUserDefaults]; 

//讀取數據到各個label中 
//讀取整型int類型的數據 
NSInteger myInteger = [userDefaultes integerForKey:@"myInteger"]; 
txtInteger.text = [NSString stringWithFormat:@"%d",myInteger]; 

//讀取浮點型float類型的數據 
float myFloat = [userDefaultes floatForKey:@"myFloat"]; 
txtFloat.text = [NSString stringWithFormat:@"%f",myFloat]; 

//讀取double類型的數據 
double myDouble = [userDefaultes doubleForKey:@"myDouble"]; 
txtDouble.text = [NSString stringWithFormat:@"%f",myDouble]; 

//讀取NSString類型的數據 
NSString *myString = [userDefaultes stringForKey:@"myString"]; 
txtNSString.text = myString; 

//讀取NSDate日期類型的數據 
NSDate *myDate = [userDefaultes valueForKey:@"myDate"]; 
NSDateFormatter *df = [[NSDateFormatter alloc] init]; 
[df setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; 
txtNSDate.text = [NSString stringWithFormat:@"%@",[df stringFromDate:myDate]]; 

//讀取數組NSArray類型的數據 
NSArray *myArray = [userDefaultes arrayForKey:@"myArray"]; 
NSString *myArrayString = [[NSString alloc] init]; 
for(NSString *str in myArray) 
NSLog(@"str= %@",str); 
myArrayString = [NSString stringWithFormat:@"%@ %@", myArrayString, str]; 
[myArrayString stringByAppendingString:str]; 
// [myArrayString stringByAppendingFormat:@"%@",str]; 
NSLog(@"myArrayString=%@",myArrayString); 
txtNSArray.text = myArrayString; 

//讀取字典類型NSDictionary類型的數據 
NSDictionary *myDictionary = [userDefaultes dictionaryForKey:@"myDictionary"]; 
NSString *myDicString = [NSString stringWithFormat:@"name:%@, age:%d",[myDictionary valueForKey:@"name"], [[myDictionary valueForKey:@"age"] integerValue]]; 
txtNSDictionary.text = myDicString; 
}

在block中要使用self的正確方式

//在進去之前要把它設為weak,讓self消失時,block中的self也跟著消失
//在進之之後要把它設為strong,讓self在block執行時,不會自行消失
__weak typeof(self) weakSelf = self;
    [_bleManager setReadingValueCompletion:^(NSData *responseData) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        strongSelf.showResult.text = [NSString stringWithFormat:@"%@",responseData];
        [strongSelf.receivedData appendData:responseData];

    }];

//在swift中使用
weak let weakSelf = self
或者在closure開頭加上[weak self]
在closure裡就直接使用self?
例如
dispatch_async(dispatch_get_main_queue(), { [weak self] () -> Void in
            self?.chatMessageTableView.reloadData()

        })

2013年12月24日 星期二

從某字串以後重新抓取字串

NSRange search = [value rangeOfString:@"#*s$"];
value = [value substringFromIndex:search.location];

直接宣告為singleton的.h

到時只要import這個.h,就可以用這個方法宣告

#define SYNTHESIZE_SINGLETON_FOR_HEADER(className) \
\
+ (className *)shared##className;

#define SYNTHESIZE_SINGLETON_FOR_CLASS(className) \
\
+ (className *)shared##className { \
static className *shared##className = nil; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
shared##className = [[self alloc] init]; \
}); \
return shared##className; \

}

iOS7似乎是little-endian

因為我一開始想對<dd07>做swap之後再印出數值,結果是65XXX多少的,後來把swap拿掉,就印出我真正想要的數字2013

//以下為程式碼
    NSData *myData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Test" ofType:@"bin"]];
    
    NSLog(@"myData:%@",myData);
    NSRange range = NSMakeRange(0, 2);
    NSLog(@"year data:%@",[myData subdataWithRange:range].description);
    NSData* yearData = [myData subdataWithRange:range];
    unsigned short x;
    [yearData getBytes:&x length:sizeof(x)];
    NSLog(@"x:%x",x);
    //unsigned short year = [self swap:x];
    NSLog(@"year:%hu",x);

year data:<dd07>
x:7dd
year:2013

-(UInt16) swap:(UInt16)s {
    NSLog(@"swap");
    UInt16 temp = s << 8;
    NSLog(@"%x",temp);
    temp |= (s >> 8);
    NSLog(@"%x",temp);
    return temp;
}

//20140207發現
iOS7在寫入時,不會自動換成little-endian
所以結論是,
讀取時,是以little-endian的方式直接讀取data
寫入時,是以data的格式直接寫入,所以要先swap成little-endian的排列,再寫入。

用NSData直接操作binary data

//下列這五個是你會用到的NSData方法
//將另一個NSData加到原來的NSData後面
-(void)appendData:(NSData*)otherData

//將bytes直接加入NSData,參數為1.bytes的起始位置(要加&),2.bytes的長度
-(void)appendBytes:(const void*)bytes length:(NSUInteger)length

//截取其中某一段NSData
-(NSData*)subdataWithRange:(NSRange)range

//從你的NSData中copy某段bytes出來,存到你新建的buffer(要加&)中(存數值資料,而非NSData)
-(void)getBytes:(void*)buffer range:(NSRange)range

//從你的NSData中從頭copy某段指定長度的bytes出來,存到你新建的buffer(要加&)中(存數值資料,而非NSData)
-(void)getBytes:(void*)buffer length:(NSUInteger)length

//底下是一些範例
//直接取得binary檔案
NSURL *imgPath = [[NSBundle mainBundle] URLForResource:@"yourBinary" withExtension:@"bin"];
        NSString *stringPath = [imgPath absoluteString];

        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:stringPath]];

//要把NSData轉回NSNumber時(這裡是以short為範例)
  short s;
  [yourData getBytes:&s range:range];
  NSNumber* stepCount = [NSNumber numberWithUnsignedShort:s];


// low level read method - read data while there is data and space available in the input buffer
- (void)_readData
{
    uint8_t buf[EAD_INPUT_BUFFER_SIZE];
    while ([[_session inputStream] hasBytesAvailable])
    {
        NSInteger bytesRead = [[_session inputStream] read:buf maxLength:EAD_INPUT_BUFFER_SIZE];
        if (_readData == nil)
        {
            _readData = [[NSMutableData alloc] init];
        }
        [_readData appendBytes:(void *)buf length:bytesRead];
        //NSLog(@"read %d bytes from input stream", bytesRead);
    }
    
    [[NSNotificationCenter defaultCenter] postNotificationName:EADSessionDataReceivedNotification object:self userInfo:nil];

    //清空_readData
    if( _readData )
    {
        [_readData setLength:0];
    }
}


// high level read method
// 這裡輸入的參數為你想要切的長度。
- (NSData *)readData:(NSUInteger)bytesToRead
{
    NSData *data = nil;
    if ([_readData length] >= bytesToRead)
    {
        NSRange range = NSMakeRange(0, bytesToRead);
        data = [_readData subdataWithRange:range];
        [_readData replaceBytesInRange:range withBytes:NULL length:0];
    }
    return data;
}

// get number of bytes read into local buffer

-(NSUInteger)readBytesAvailable
{
    return [_readData length];

}

//自己寫的切封包
+ (NSArray*)splitDataIntoChunks:(NSData*)data size:(NSUInteger)bytesPerChunk{
    
    if(!data) return nil;
    
    NSMutableArray* array = [[NSMutableArray alloc]init];
    NSUInteger dataLength = [data length];
    NSUInteger chunkCount = 0;
    while (chunkCount < dataLength)
    {
        NSRange range = NSMakeRange(chunkCount, bytesPerChunk);
        NSData* chunk = [data subdataWithRange:range];
        [array addObject:chunk];
        chunkCount += 2;
    }

    return array;

}

2013年12月23日 星期一

取得檔案的二種方法nsbundle & nsfilehandle

NSString *newPath=[[NSBundle mainBundle] pathForResource:@"test" ofType:@"txt"];

NSFileHandle *fileHandle=[NSFileHandle fileHandleForReadingAtPath:newPath];

[[NSBundle mainBundle] pathForResource:@"test" ofType:@"jat"];

2013年12月20日 星期五

EAAccessory的Properties代表的資訊

//這是連上Device時,iOS指定的一個連線ID,斷線之後就不存在
//如果有二台以上同型的Device,你可以用connectionID來分別它們
_accessory.connectionID : 23431639
//Device的製造商名稱
_accessory.manufacturer : Acme
//Device的顯示名稱
_accessory.name : iAP Auth Only
//Device的模組資訊
_accessory.modelNumber : Acme 3.0
//Device的出廠序號
_accessory.serialNumber : 2.71828182
//Device的F/W版本
_accessory.firmwareRevision : 3.0.0
//Device的硬體版本
_accessory.hardwareRevision : 4.1.0

//由下列source code讀出
-(void)printProperties
{
    NSLog(@"_accessory.connectionID : %i", _accessory.connectionID);
    NSLog(@"_accessory.manufacturer : %@", _accessory.manufacturer);
 
    NSLog(@"_accessory.name : %@", _accessory.name);
    NSLog(@"_accessory.modelNumber : %@", _accessory.modelNumber);
    NSLog(@"_accessory.serialNumber : %@", _accessory.serialNumber);
    NSLog(@"_accessory.firmwareRevision : %@", _accessory.firmwareRevision);
    NSLog(@"_accessory.hardwareRevision : %@", _accessory.hardwareRevision);
 
}

2013年12月19日 星期四

c語言中的波折號

是一個位元運算符號

是將某值換成位元之後,取not值(即0->1;1->0)

解決capturing self strongly in this block is likely to lead to a retain cycle的問題

__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                     queue:nil
                                usingBlock:^(CMTime time) {
                                                current+=1;

                                                if(current==60)
                                                {
                                                    min+=(current/60);
                                                    current = 0;
                                                }

                                                 [weakSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
                                            }];

較嚴格的singleton寫法(封住init)

//利用dispatch_once來寫,會比@synchronized(self)快上三倍,但如果instance被消除,前言不會重新init一次,但外部本來就無法消除內部的instance,所以小心不要在內部消除即可
//自己的範例
#pragma mark - Initial with Singleton
+(instancetype) sharedInstance {
    static dispatch_once_t pred;
    static CBManager *instance = nil;
    dispatch_once(&pred, ^{instance = [[self alloc] initSingleton];});
    return instance;
}
- (id)init {
    return nil;
}
- (id)initSingleton {
    self = [super init];
    if ( self ) {
        //TODO: ...
    }
    return self;
}

//網路上的範例
+ (MYSingleton *)sharedSingleton {
     static dispatch_once_t pred;
     static MYSingleton *instance = nil;
     dispatch_once(&pred, ^{instance = [[self alloc] initSingleton];});
     return instance;
}
- (id)init {
    // Forbid calls to –init or +new
    NSAssert(NO, @”Cannot create instance of Singleton);
    // You can return nil or [self initSingleton] here, 
    // depending on how you prefer to fail.
    return nil;
}
// Real (private) init method
- (id)initSingleton {
    self = [super init];
    if ((self = [super init])) {
    // Init code }
    return self;
}

2013年12月17日 星期二

External Accessory Framework連線

//方法一   跳出手動點選的連線視窗
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self CONTAINS 'MyString'"];

[[EAAccessoryManager sharedAccessoryManager] showBluetoothAccessoryPickerWithNameFilter:predicate completion:nil]


//方法二   程式回連
//先用這個取回現正連線的裝置,他會回傳NSArray
[[EAAccessoryManager sharedAccessoryManager]connectedAccessories ];
//然後在Array中取出你想連線的裝置
//最後用這個去打開session即可使用
_session = [[EASession alloc] initWithAccessory:_accessory forProtocol:MFI_BUNDLE_ID];

消鍵盤的三種方式

//一次把view上面的所有text field的鍵盤都消完
[self.view endEditing:YES];

//針對某個text field去消鍵盤
[self.textBox resignFirstResponder];

//如果要用鍵盤上return key的方式,要去實作textFiled的did end on exit的方法
再在方法中加入以上消鍵盤的方法即可
//或者在離開textField的時候實作
- (IBAction)TextField_DidEndOnExit:(id)sender {
    [sender resignFirstResponder];
}

P.S.消鍵盤的設定,都要在main thread完成,不然會導致app crash.
*** Assertion failure in -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished], /SourceCache/UIKit/UIKit-2935.138/Keyboard/UIKeyboardTaskQueue.m:368
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'

2013年12月16日 星期一

抓iPhone的手機名稱

NSString* deviceName = [[NSString alloc]init];
deviceName = [[UIDevice currentDevice] name];

2013年12月13日 星期五

取絕對值

//取整數的絕對值
abs(int i);

//取float的絕對值
fabsf(float i);

//取double的絕對值
fabs(double i);

2013年12月12日 星期四

UIAlertView的使用

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"didUpdateToLocation"//設定標題
message:[NSString stringWithFormat:@"%f",_heading.trueHeading ]  //設定警告訊息內文
                                                   delegate:self // 要用那一個物件來處理按鈕的delegate事件
                          
cancelButtonTitle:@"OK"  //cancel按鈕文字的設定
otherButtonTitles: nil]; // 設定其他按鈕title
// 如果要多個其他按鈕 >> otherButtonTitles: @"check1", @"check2", nil];
    
    [alert show];  // alert這個物件秀出來
          alert.tag = 0; //當一個View有多個alertView時,可以用來區分
    [alert release];  // 把物件釋放(MRC環境下)
}



//2秒後關alert view
double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [alert dismissWithClickedButtonIndex:0 animated:NO];

    });


//包成method
-(void)_showAlertViewWithTitle:(NSString*)title content:(NSString*)content showTime:(float)showTime{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
                                                    message:content
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"  //cancel按鈕文字的設定
                                          otherButtonTitles: nil]; // 設定其他按鈕title
    // 如果要多個其他按鈕 >> otherButtonTitles: @"check1", @"check2", nil];
    [alert setTag:0];
    [alert show];  // alert這個物件秀出來
    
    //2秒後關alert view
    double delayInSeconds = showTime;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        if (showTime != 0.0) {
            [alert dismissWithClickedButtonIndex:0 animated:NO];
        }
    });

}


#pragma marks -- UIAlertViewDelegate --
//用Delegate實現alertView的按鍵功能
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    NSLog(@"clickButtonAtIndex:%d",buttonIndex);
}

//AlertView已經消失時的事件
-(void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
    NSLog(@"didDismissWithButtonIndex");
}

//ALertView即將消失時的事件
-(void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    NSLog(@"willDismissWithButtonIndex");
}

//AlertView的取消按鍵的事件
-(void)alertViewCancel:(UIAlertView *)alertView
{
    NSLog(@"alertViewCancel");
}

//AlertView已經顯示時的事件
-(void)didPresentAlertView:(UIAlertView *)alertView
{
    NSLog(@"didPresentAlertView");
}

//AlertView即將顯示時的事件
-(void)willPresentAlertView:(UIAlertView *)alertView
{
    NSLog(@"willPresentAlertView");
}  

2013年12月11日 星期三

抓最新位置的機制

1,每次更新時,觸發delegate,把最近位置放到Add到Array的最後一個
2,再用lastObject取出最新的location

2013年12月9日 星期一

如果NSMutableArray裡沒有就加入

//如果NSMutableArray裡沒有就加入
if(![self.dicoveredPeripherals containsObject:peripheral])
        [self.dicoveredPeripherals addObject:peripheral];


//如果NSMutableArray裡有就移除
if([self.dicoveredPeripherals containsObject:peripheral])

        [self.dicoveredPeripherals removeObject:peripheral];

new = alloc + init

self.characteristicsCBUUID = [NSMutableDictionary new];

等於

self.characteristicsCBUUID = [[NSMutableDictionary alloc]init];

模仿官方版的註解格式,就可以在右邊的quick helper看到註解

手寫的註解
/*!
 *  @method swap:
 *
 *  @param s Uint16 value to byteswap
 *
 *  @discussion swap byteswaps a UInt16 
 *
 *  @return Byteswapped UInt16
 */

-(UInt16) swap:(UInt16)s {
    UInt16 temp = s << 8;
    temp |= (s >> 8);
    return temp;

}


官方版的註解
/*!
 *  @method writeValue:forCharacteristic:type:
 *
 *  @param data The value to write.
 *  @param characteristic The characteristic whose characteristic value will be written.
 *  @param type The type of write to be executed.
 *
 *  @discussion Writes <i>value</i> to <i>characteristic</i>'s characteristic value. If the <code>CBCharacteristicWriteWithResponse</code>
 * type is specified, {@link peripheral:didWriteValueForCharacteristic:error:} is called with the result of the write request.
 *
 *  @see peripheral:didWriteValueForCharacteristic:error:
 * @see CBCharacteristicWriteType
 */
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;

override "init" in objective-c

- (id)init {
    self = [super init];
    if (self) {
        [self resetCentralManager];
    }
    return self;

}

-(void)resetCentralManager{
    // Will callack on centralManagerDidUpdateState delegate
    _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    _data = [[NSMutableData alloc] init];
}

2013年12月8日 星期日

init與initialize的分別

initialize是在你初始化時,對你的class先做初始化

init是你對class做完初始化之後,對你的instance做初始化

所以我們對同一個class建立三個instance時

initialize只會在一開始被執行一次,init會在之後被執行三次

2013年12月5日 星期四

去除註解(利用正則表示式)

到xcode左邊,選搜尋的地方,選正則表示式,再取代成空值

去範圍註解
/\*.*[\*][\/]


去單行註解
//.*$

2013年12月3日 星期二

判斷iOS7

+(BOOL)isIOS7
{
    return ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f);

}

縮放圖片

+(UIImage *)scaleImage:(UIImage *)_image size:(CGSize)_size
{
    CGFloat _toWidth  = _size.width;
    CGFloat _toHeight = _size.height;
    float _x = 0.0f;
    float _y = 0.0f;
    CGRect _frame = CGRectMake(_x, _y, _toWidth, _toHeight);
    float _oldWidth  = _image.size.width;
    float _oldHeight = _image.size.height;
    float _scaleRatio   = MAX( (_toWidth / _oldWidth), (_toHeight / _oldHeight) );
    float _equalWidth   = (int)( _oldWidth * _scaleRatio );
    float _equalHeight  = (int)( _oldHeight * _scaleRatio );
    UIGraphicsBeginImageContext( CGSizeMake(_equalWidth, _equalHeight) );
    [_image drawInRect:CGRectMake(0, 0, _equalWidth, _equalHeight)];
    _image = nil;
    _image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    _x = floor( (_equalWidth -  _toWidth) / 2 );
    _y = floor( (_equalHeight - _toHeight) / 2 );
    _frame = CGRectMake(_x, _y, _toWidth, _toHeight);
    CGImageRef _smallImage = CGImageCreateWithImageInRect( [_image CGImage], _frame );
    UIImage *_doneImage    = [UIImage imageWithCGImage:_smallImage];
    CGImageRelease(_smallImage);
    return _doneImage;
}

2013年12月2日 星期一

寫個timer來用

#pragma mark- Update by Timer Trigger
//對外接口
-(void) switchTransferDataTimerStatus:(BOOL)doYouWannaTurnOnTimer{
    if (doYouWannaTurnOnTimer) {
        [self _startTransferTimer];
    }else{
        [self _stopTransferTimer];
    }
}
//開始timer
-(void)_startTransferTimer
{
    if( !_transferTimer )
    {
        [self performSelector:@selector(_transferSportData) withObject:nil afterDelay:0.8f];
        _transferTimer = [NSTimer scheduledTimerWithTimeInterval:TranferToWatchCycleInterval
                                                               target:self
                                                             selector:@selector(_transferSportData)
                                                             userInfo:nil
                                                              repeats:YES];
    }
}
//關掉timer
-(void)_stopTransferTimer
{
    if( _transferTimer )
    {
        [_transferTimer invalidate];
        _transferTimer = nil;
    }
}

//你要用timer做的事
-(void)_transferSportData{
    
    NSLog(@"我被印出來啦");

}

2013年11月27日 星期三

NSString只留下你想要的正則表示法部份,其他取代為空字串

_phoneNumber = [strNumber stringByReplacingOccurrencesOfString:@"[^0-9]"
                                                                            withString:@""
                                                                               options:NSRegularExpressionSearch

                                                                                 range:NSMakeRange(0, [strNumber length])];

2013年11月24日 星期日

EADemo的流程

EADemo是Apple提供的一個實現MFi的範例
它主要就是在使用EADSessionController這個Class,並且把它設定成singleton,讓程式能隨時呼叫它來做藍牙傳輸的訊息收發。

/*
@ 開始使用EADSessionController前的準備動作
*/

//先打開EAAccessoryManager的通知
//讓它能在Accessory連上與斷開時,能接到通知,觸發你要的function
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_accessoryDidConnect:) name:EAAccessoryDidConnectNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_accessoryDidDisconnect:) name:EAAccessoryDidDisconnectNotification object:nil];
    [[EAAccessoryManager sharedAccessoryManager] registerForLocalNotifications];

//以singleton的模式呼叫EADSessionController
//讓它能在往後被程式所使用。
    _eaSessionController = [EADSessionController sharedController];

//用一個陣列去接目前己連線的Accessories
//讓它能在往後被使用。
    _accessoryList = [[NSMutableArray alloc] initWithArray:[[EAAccessoryManager sharedAccessoryManager] connectedAccessories]];

    if ([_accessoryList count] == 0) {
        //如果沒抓到已連接的裝置,執行這裡...
    } else {
        //如果有抓到已連接的裝置,執行這裡...

    }


/*
@ 實作接到connected / disconnected通知的function
*/
#pragma mark Internal

- (void)_accessoryDidConnect:(NSNotification *)notification {
    EAAccessory *connectedAccessory = [[notification userInfo] objectForKey:EAAccessoryKey];
    [_accessoryList addObject:connectedAccessory];

    if ([_accessoryList count] == 0) {
        [_noExternalAccessoriesPosterView setHidden:NO];
    } else {
        [_noExternalAccessoriesPosterView setHidden:YES];
    }

    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:([_accessoryList count] - 1) inSection:0];
    [[self tableView] insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}

- (void)_accessoryDidDisconnect:(NSNotification *)notification {
    EAAccessory *disconnectedAccessory = [[notification userInfo] objectForKey:EAAccessoryKey];

    if (_selectedAccessory && [disconnectedAccessory connectionID] == [_selectedAccessory connectionID])
    {
        [_protocolSelectionActionSheet dismissWithClickedButtonIndex:-1 animated:YES];
    }

    int disconnectedAccessoryIndex = 0;
    for(EAAccessory *accessory in _accessoryList) {
        if ([disconnectedAccessory connectionID] == [accessory connectionID]) {
            break;
        }
        disconnectedAccessoryIndex++;
    }

    if (disconnectedAccessoryIndex < [_accessoryList count]) {
        [_accessoryList removeObjectAtIndex:disconnectedAccessoryIndex];
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:disconnectedAccessoryIndex inSection:0];
        [[self tableView] deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
} else {
        NSLog(@"could not find disconnected accessory in accessory list");
    }

    if ([_accessoryList count] == 0) {
        //如果沒抓到已連接的裝置,執行這裡...
    } else {
        //如果有抓到已連接的裝置,執行這裡...

    }
}


/*
@ 準備動作做完,實際使用EADSessionController的物件來傳送封包
*/

//
- (void)setupControllerForAccessory:(EAAccessory *)accessory withProtocolString:(NSString *)protocolString;

- (BOOL)openSession;
- (void)closeSession;

- (void)writeData:(NSData *)data;

- (NSUInteger)readBytesAvailable;
- (NSData *)readData:(NSUInteger)bytesToRead;

2013年11月19日 星期二

把圖片轉為NSData

//png
self.dataToSend = UIImagePNGRepresentation([UIImage imageNamed:@"test.png"]);

//jpg
self.dataToSend = UIImageJPEGRepresentation([UIImage imageNamed:@"test.jpg"]);

2013年11月14日 星期四

三個步驟輕鬆完成XCode5的App OTA(Over theAir Technology)

一,你要使用企業版開發者帳號(299USD那個)(經前輩指教,這裡應該註明有in-house的帳號)

二,在包ipa時選第二個選項,在Application URL的地方填上你預計要放.ipa檔的網路位置,填好後會產生.ipa跟.plist兩個檔,記得把這兩個檔案都放到你剛剛填的網路空間。


三,把下列這行的網址部份換成你剛剛包ipa時用的網址,但要記得把.ipa換成.plist
itms-services://?action=download-manifest&url=http://yourURL/yourAppName.plist
換好後可以直接傳到你朋友的手機,或是寫一個簡單的網頁加一個超鏈結丟給他,總之就是請他用瀏覧器打開,搞定!!!

P.S. xcode 6之後好像拿掉這個選項,但經過實驗仍可以將原來的.plist中的內容改成你的ipa檔來使用。
yourAppName.plist內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>yourURL/yourAppName.ipa</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>yourBundleID</string>
<key>bundle-version</key>
<string>1.0</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>yourAppName</string>
</dict>
</dict>
</array>
</dict>
</plist>

iOS IAP(In Application Purchase) & 沙箱測試

 Himi  原创, 欢迎转载,转载请在明显处注明! 谢谢。


//——2012-12-11日更新   获取"产品付费数量等于0这个问题"的原因
看到很多童鞋问到,为什么每次都返回数量等于0??
其实有童鞋已经找到原因了,原因是你在 ItunesConnect 里的 “Contracts, Tax, and Banking 没有设置账户信息

这里也是由于Himi疏忽的原因没有说明,这里先给童鞋们带来的麻烦,致以歉意。

//——2012-6-25日更新iap恢复
看到很多童鞋说让Himi讲解如何恢复iap产品,其实博文已经给出了。这里再详细说下:
首先向AppStore请求恢复交易:
1
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
然后当用户输入正确的appStore账号密码后,进入
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions//交易结果
进入上面函数中的
1
2
3
4
5
6
case SKPaymentTransactionStateRestored://恢复
            {
                [self restoreTransaction:transaction];
            }
                break;
然后我们再以下重写函数中处理即可!
- (void) restoreTransaction: (SKPaymentTransaction *)transaction
//——-


       终于在11月公司的游戏即将上线了,那么对于iOS游戏来说当今都是内置道具收费属于主流,那么我们的游戏也是内置收费,所以Himi这里分享给大家关于内置应用收费以及申请测试账号进行测试购买的经验;
      在应用内嵌入付费代码这一快Himi可以直接将代码分享给大家,所以我们来说一些主要流程,毕竟没有接触过这一块的童鞋肯定相当头疼 =。  =
     OK,步入整体,如果你想在iOS里内嵌收费,那么分为以下几步:
            
             【提示:以下创建App部分内容,你不用非要等项目能打包了才开始做,可以随时并且随便的创建个测试项目即可,因为嵌入付费并不要求上传App的ipa包的!!】          

     第一步:你需要在iTunesConnect中创建个新的App,然后为这个App设置一些产品(付费道具)等;
     OK,这里Himi稍微解释下,iTunesConnect是苹果提供的一个平台,主要提供AP发布和管理App的,最重要的功能是创建管理项目信息,项目付费产品(道具)管理、付费的测试账号、提交App等等,这里就简单介绍这么多,关于产品一词在此我们可以理解成游戏道具即可;在苹果看来所有付费都属于产品 =。 =千万不要纠结字眼哦~
    OK,打开iTunesConnect网站:https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa (注意:企业级的用户必须使用公司主开发者账号登陆才可!)
    成功登陆后的页面如下:

              
              这里大概说下重要的一些项:
             Contracts, Tax, and Banking   : 管理银行账号、联系人以及税等等;这里要根据提示完成对应的信息填写!一定要详细填写喔~
             Manage Users :管理用户的,比如主账号以及测试付费的(测试App)账号;
             Manage Your Applictions:管理应用程序的,你所有发布的应用和每个应用的状态都在这里面;

     下面我们新建一个App项目,大家放心,我们这里创建的是不会直接提交给App审核的,所以放心创建,只要控制好App的状态不要是待审核状态即可,不过即使你不小心将项目提交了,也没事,直接更改App状态即可了;
     选择Manage Your Applictions选项,然后新建一个项目:【Add New App】,根据提示来填写吧,这里就不细致说明了~
     创建好一个App之后,在点击Manage Your Applictions后的界面应该如下:
     
    这里你将看到自己创建的App,点击你创建的App项目,这里Himi创建的项目名字叫”ProjectForBuyTest“,点击你的App进入如下界面:

    
  (注意:这里的Bundle ID一定要跟你的项目中的info.plist中的Bundle ID保证一致!!!!)
     这里可以管理你的项目的信息、状态、是否嵌入GameCenter等等选项,那么本章我们重点介绍如何使用IAp沙盒测试程序内付费,所以这里我们点击右上角的”Manage In-App Purchases“选项进入创建产品(游戏道具)界面如下:

      上图中的下方看到Himi创建过的四个产品(道具)了,你可以点击”Create New“选项新建一个产品(付费道具),点击新建如下界面:
  
   上图中Himi没有截图出所有的选项,这里大概介绍下,这个界面是选择你的消费道具的种类,种类说明如下:
   类型选择有四种选择:
   1.Consumable(消耗品): 每次下载都需要付费;
   2.Non-consumable(非消耗品): 仅需付费一次;
   3.Auto-Renewable Subscriptions:自动订阅;
   4.Free Subscription:免费订阅
   最下方是你沙盒测试的截图,暂且不管即可;
   这里Himi选择Consumable选项,比如很多游戏都是购买金币啦这样子就可以选择这个;然后出现如下界面:

   Reference Name: 付费产品(道具的)参考名称
   Product ID(产品ID): 你产品的唯一id。通常格式是 com.xx.yy,但它可以是任何形式,不要求以程序的App ID作为前缀。
   Add Language: 添加产品名称与描述语言;
   Price Tier:选择价格,这里你选择价格后,会出现如上图最下方的价格对照表
   Screenshot(截屏): 展示你产品的截屏。(这个直接无视,测试App务必要管这个的)

  Product ID(产品ID)可以创建多个,比如我想游戏中分为0.99$ 、1.99$等道具那就创建对应多个产品ID
  我们填写好了”Reference Name“与”Product ID“以及”Price Tier“后,点击”Add Language“选项然后出现如下界面:
            

  上图中的选项:
      Language:语言
      Displayed Name(显示名称): 用户看到的产品名称。
      Description(描述): 对产品进行描述。
  
  Ok,一路 Save保存回到”Manage In-App Purchases“界面中会看到我们新建的产品(道具)如下:
 

  大家可以看到新建的产品(道具)ID:这里Himi创建的产品ID是com.himi.wahaha ,这里要记住这个产品ID哦~

第二步:申请测试账号,利用沙盒测试模拟AppStore购买道具流程!
  回到itunesconnect主页中,选择“Manage Users”然后选择“Test User”,然后出现的界面如下图:
    这里Himi已经创建了两个测试账号了,点击界面中的 “Add New User”进行创建即可;记住账号和密码哈,记不住就删掉重新建 娃哈哈~(切记:不能用于真正的AppStore中使用此账号,不仅不能用,而且一旦AppStore发现后果你懂得~) 

   第三步:在项目中申请购买产品代码以及监听;
         这里关于购买的代码部分呢,我都有备注的,Himi这里就不详细讲解了,Himi只是在代码后介绍几点值得注意的地方:

 这里Himi是新建的一个Cocos2d的项目,然后给出HelloWorldLayer.h以及HelloWorldLayer.m的全部代码,所有购买代码也全在里面也对应有Himi的注释!
          HelloWorldLayer.h
  1. //  
  2. //  HelloWorldLayer.h  
  3. //  buytest  
  4. //  
  5. //  Created by 华明 李 on 11-10-29.  
  6. //  Copyright Himi 2011年. All rights reserved.  
  7. //  
  8.   
  9.   
  10. // When you import this file, you import all the cocos2d classes  
  11. #import "cocos2d.h"  
  12. #import <UIKit/UIKit.h>  
  13.   
  14. #import <StoreKit/StoreKit.h>  
  15. enum{  
  16.      IAP0p99=10,  
  17.      IAP1p99,  
  18.      IAP4p99,  
  19.      IAP9p99,   
  20.      IAP24p99,  
  21. }buyCoinsTag;   
  22.   
  23. @interface HelloWorldLayer : CCLayer<SKProductsRequestDelegate,SKPaymentTransactionObserver>  
  24. {  
  25.     int buyType;  
  26. }  
  27.   
  28. +(CCScene *) scene;    
  29. - (void) requestProUpgradeProductData;  
  30. -(void)RequestProductData;  
  31. -(bool)CanMakePay;                               
  32. -(void)buy:(int)type;   
  33. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;  
  34. -(void) PurchasedTransaction: (SKPaymentTransaction *)transaction;  
  35. - (void) completeTransaction: (SKPaymentTransaction *)transaction;  
  36. - (void) failedTransaction: (SKPaymentTransaction *)transaction;  
  37. -(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction;  
  38. -(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error;  
  39. - (void) restoreTransaction: (SKPaymentTransaction *)transaction;  
  40. -(void)provideContent:(NSString *)product;  
  41. -(void)recordTransaction:(NSString *)product;  
  42. @end  
     
             HelloWorldLayer.m
  1. //    
  2. //  IapLayer.m    
  3. //    
  4. //  Created by Himi on 11-5-25.    
  5. //  Copyright 2011年 李华明 . All rights reserved.    
  6. //    
  7.     
  8. #import "HelloWorldLayer.h"     
  9. #define ProductID_IAP0p99 @"com.buytest.one"//$0.99      
  10. #define ProductID_IAP1p99 @"com.buytest.two" //$1.99     
  11. #define ProductID_IAP4p99 @"com.buytest.three" //$4.99      
  12. #define ProductID_IAP9p99 @"com.buytest.four" //$19.99      
  13. #define ProductID_IAP24p99 @"com.buytest.five" //$24.99      
  14.     
  15. @implementation HelloWorldLayer    
  16. +(CCScene *) scene    
  17. {    
  18.     CCScene *scene = [CCScene node];    
  19.     HelloWorldLayer *layer = [HelloWorldLayer node];    
  20.     [scene addChild: layer];    
  21.     return scene;    
  22. }    
  23. -(id)init    
  24. {    
  25.     if ((self = [super init])) {    
  26.         CGSize size = [[CCDirector sharedDirector] winSize];    
  27.         CCSprite *iap_bg  = [CCSprite spriteWithFile:@"Icon.png"];      
  28.         [iap_bg setPosition:ccp(size.width/2,size.height/2)];    
  29.         [self addChild:iap_bg z:0];    
  30.         //---------------------    
  31.         //----监听购买结果    
  32.         [[SKPaymentQueue defaultQueue] addTransactionObserver:self];    
  33.         //申请购买    
  34.         /*  
  35.          enum{  
  36.          IAP0p99=10,  
  37.          IAP1p99,  
  38.          IAP4p99,  
  39.          IAP9p99,  
  40.          IAP24p99,  
  41.          }buyCoinsTag;  
  42.          */    
  43.         [self buy:IAP24p99];    
  44.     }    
  45.     return self;    
  46. }    
  47.     
  48. -(void)buy:(int)type    
  49. {     
  50.     buyType = type;      
  51.     if ([SKPaymentQueue canMakePayments]) {    
  52.         //[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];    
  53.         [self RequestProductData];      
  54.         CCLOG(@"允许程序内付费购买");    
  55.     }    
  56.     else    
  57.     {    
  58.         CCLOG(@"不允许程序内付费购买");     
  59.         UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:@"Alert"     
  60.                                                             message:@"You can‘t purchase in app store(Himi说你没允许应用程序内购买)"                                                            
  61.                                                            delegate:nil cancelButtonTitle:NSLocalizedString(@"Close(关闭)",nil) otherButtonTitles:nil];    
  62.             
  63.         [alerView show];    
  64.         [alerView release];    
  65.             
  66.     }     
  67. }    
  68.      
  69. -(bool)CanMakePay    
  70. {    
  71.     return [SKPaymentQueue canMakePayments];    
  72. }    
  73.     
  74. -(void)RequestProductData    
  75. {    
  76.     CCLOG(@"---------请求对应的产品信息------------");    
  77.     NSArray *product = nil;    
  78.     switch (buyType) {    
  79.         case IAP0p99:    
  80.             product=[[NSArray alloc] initWithObjects:ProductID_IAP0p99,nil];    
  81.             break;    
  82.         case IAP1p99:    
  83.             product=[[NSArray alloc] initWithObjects:ProductID_IAP1p99,nil];    
  84.             break;    
  85.         case IAP4p99:    
  86.             product=[[NSArray alloc] initWithObjects:ProductID_IAP4p99,nil];    
  87.             break;    
  88.         case IAP9p99:    
  89.             product=[[NSArray alloc] initWithObjects:ProductID_IAP9p99,nil];    
  90.             break;    
  91.         case IAP24p99:    
  92.             product=[[NSArray alloc] initWithObjects:ProductID_IAP24p99,nil];    
  93.             break;    
  94.                 
  95.         default:    
  96.             break;    
  97.     }    
  98.     NSSet *nsset = [NSSet setWithArray:product];    
  99.     SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset];    
  100.     request.delegate=self;    
  101.     [request start];    
  102.     [product release];    
  103. }    
  104. //<SKProductsRequestDelegate> 请求协议    
  105. //收到的产品信息    
  106. - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{    
  107.         
  108.     NSLog(@"-----------收到产品反馈信息--------------");    
  109.     NSArray *myProduct = response.products;    
  110.     NSLog(@"产品Product ID:%@",response.invalidProductIdentifiers);    
  111.     NSLog(@"产品付费数量: %d", [myProduct count]);    
  112.     // populate UI     
  113.     for(SKProduct *product in myProduct){    
  114.         NSLog(@"product info");    
  115.         NSLog(@"SKProduct 描述信息%@", [product description]);       
  116.         NSLog(@"产品标题 %@" , product.localizedTitle);    
  117.         NSLog(@"产品描述信息: %@" , product.localizedDescription);    
  118.         NSLog(@"价格: %@" , product.price);    
  119.         NSLog(@"Product id: %@" , product.productIdentifier);     
  120.     }     
  121.     SKPayment *payment = nil;     
  122.     switch (buyType) {    
  123.         case IAP0p99:    
  124.             payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP0p99];    //支付$0.99    
  125.             break;    
  126.         case IAP1p99:    
  127.             payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP1p99];    //支付$1.99    
  128.             break;    
  129.         case IAP4p99:    
  130.             payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP4p99];    //支付$9.99    
  131.             break;    
  132.         case IAP9p99:    
  133.             payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP9p99];    //支付$19.99    
  134.             break;    
  135.         case IAP24p99:    
  136.             payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP24p99];    //支付$29.99    
  137.             break;    
  138.         default:    
  139.             break;    
  140.     }    
  141.     CCLOG(@"---------发送购买请求------------");    
  142.     [[SKPaymentQueue defaultQueue] addPayment:payment];      
  143.     [request autorelease];     
  144.         
  145. }    
  146. - (void)requestProUpgradeProductData    
  147. {    
  148.     CCLOG(@"------请求升级数据---------");    
  149.     NSSet *productIdentifiers = [NSSet setWithObject:@"com.productid"];    
  150.     SKProductsRequest* productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];    
  151.     productsRequest.delegate = self;    
  152.     [productsRequest start];     
  153.         
  154. }    
  155. //弹出错误信息    
  156. - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{    
  157.     CCLOG(@"-------弹出错误信息----------");    
  158.     UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Alert",NULL) message:[error localizedDescription]    
  159.                                                        delegate:nil cancelButtonTitle:NSLocalizedString(@"Close",nil) otherButtonTitles:nil];    
  160.     [alerView show];    
  161.     [alerView release];    
  162. }    
  163.     
  164. -(void) requestDidFinish:(SKRequest *)request     
  165. {    
  166.     NSLog(@"----------反馈信息结束--------------");    
  167.         
  168. }    
  169.      
  170. -(void) PurchasedTransaction: (SKPaymentTransaction *)transaction{    
  171.     CCLOG(@"-----PurchasedTransaction----");    
  172.     NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil];    
  173.     [self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions];    
  174.     [transactions release];    
  175. }     
  176.     
  177. //<SKPaymentTransactionObserver> 千万不要忘记绑定,代码如下:    
  178. //----监听购买结果    
  179. //[[SKPaymentQueue defaultQueue] addTransactionObserver:self];    
  180.     
  181. - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions//交易结果    
  182. {    
  183.     CCLOG(@"-----paymentQueue--------");    
  184.     for (SKPaymentTransaction *transaction in transactions)    
  185.     {    
  186.         switch (transaction.transactionState)    
  187.         {     
  188.             case SKPaymentTransactionStatePurchased://交易完成     
  189.                 [self completeTransaction:transaction];    
  190.                 CCLOG(@"-----交易完成 --------");    
  191.                 CCLOG(@"不允许程序内付费购买");     
  192.                 UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:@"Alert"     
  193.                                                                     message:@"Himi说你购买成功啦~娃哈哈"                                                          
  194.                                                                    delegate:nil cancelButtonTitle:NSLocalizedString(@"Close(关闭)",nil) otherButtonTitles:nil];    
  195.                     
  196.                 [alerView show];    
  197.                 [alerView release];     
  198.                 break;     
  199.             case SKPaymentTransactionStateFailed://交易失败     
  200.                 [self failedTransaction:transaction];    
  201.                  CCLOG(@"-----交易失败 --------");    
  202.                 UIAlertView *alerView2 =  [[UIAlertView alloc] initWithTitle:@"Alert"     
  203.                                                                     message:@"Himi说你购买失败,请重新尝试购买~"                                                          
  204.                                                                    delegate:nil cancelButtonTitle:NSLocalizedString(@"Close(关闭)",nil) otherButtonTitles:nil];    
  205.                     
  206.                 [alerView2 show];    
  207.                 [alerView2 release];    
  208.                 break;     
  209.             case SKPaymentTransactionStateRestored://已经购买过该商品     
  210.                 [self restoreTransaction:transaction];    
  211.                  CCLOG(@"-----已经购买过该商品 --------");    
  212.             case SKPaymentTransactionStatePurchasing:      //商品添加进列表    
  213.                  CCLOG(@"-----商品添加进列表 --------");    
  214.                 break;    
  215.             default:    
  216.                 break;    
  217.         }    
  218.     }    
  219. }    
  220. - (void) completeTransaction: (SKPaymentTransaction *)transaction    
  221.     
  222. {    
  223.     CCLOG(@"-----completeTransaction--------");    
  224.     // Your application should implement these two methods.    
  225.     NSString *product = transaction.payment.productIdentifier;    
  226.     if ([product length] > 0) {    
  227.             
  228.         NSArray *tt = [product componentsSeparatedByString:@"."];    
  229.         NSString *bookid = [tt lastObject];    
  230.         if ([bookid length] > 0) {    
  231.             [self recordTransaction:bookid];    
  232.             [self provideContent:bookid];    
  233.         }    
  234.     }    
  235.         
  236.     // Remove the transaction from the payment queue.    
  237.         
  238.     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];    
  239.         
  240. }    
  241.     
  242. //记录交易    
  243. -(void)recordTransaction:(NSString *)product{    
  244.     CCLOG(@"-----记录交易--------");    
  245. }    
  246.     
  247. //处理下载内容    
  248. -(void)provideContent:(NSString *)product{    
  249.     CCLOG(@"-----下载--------");     
  250. }    
  251.     
  252. - (void) failedTransaction: (SKPaymentTransaction *)transaction{    
  253.     NSLog(@"失败");    
  254.     if (transaction.error.code != SKErrorPaymentCancelled)    
  255.     {    
  256.     }    
  257.     [[SKPaymentQueue defaultQueue] finishTransaction: transaction];    
  258.         
  259.         
  260. }    
  261. -(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction{    
  262.         
  263. }    
  264.     
  265. - (void) restoreTransaction: (SKPaymentTransaction *)transaction    
  266.     
  267. {    
  268.     NSLog(@" 交易恢复处理");    
  269.         
  270. }    
  271.     
  272. -(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error{    
  273.     CCLOG(@"-------paymentQueue----");    
  274. }    
  275.     
  276.     
  277. #pragma mark connection delegate    
  278. - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data    
  279. {    
  280.     NSLog(@"%@",  [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);     
  281. }    
  282. - (void)connectionDidFinishLoading:(NSURLConnection *)connection{    
  283.         
  284. }    
  285.     
  286. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{    
  287.     switch([(NSHTTPURLResponse *)response statusCode]) {    
  288.         case 200:    
  289.         case 206:    
  290.             break;    
  291.         case 304:     
  292.             break;    
  293.         case 400:     
  294.             break;      
  295.         case 404:    
  296.             break;    
  297.         case 416:    
  298.             break;    
  299.         case 403:    
  300.             break;    
  301.         case 401:    
  302.         case 500:    
  303.             break;    
  304.         default:    
  305.             break;    
  306.     }            
  307. }    
  308.     
  309. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {    
  310.     NSLog(@"test");    
  311. }    
  312.     
  313. -(void)dealloc    
  314. {    
  315.     [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];//解除监听  
  316.     [super dealloc];    
  317. }     
  318. @end  



    代码注释的相当清楚了,没有什么可解释的,这里说几点值得注意的地方:
1.添加对应对应代码时不要忘记,添加框架 StoreKit.framework,如何添加框架请看我的博文【iOS-Cocos2d游戏开发之十四】音频/音效/视频播放(利用Cocos2D-iPhone-Extensions嵌入Cocos2d进行视频播放!)
2. 越狱机器无法沙盒测试!模拟器的话,Himi用4.3模拟器不可以,因为提示没有开启程序内付费- -(我都没看到模拟器有store的选项,so~);但是使用iOS5的模拟器可以测试沙盒,但是执行的顺序会有些问题,但是还没真机的童鞋可以使用,建议一切以真机实测为准
3. 千万不要忘记在iTunesConnect中创建App Bundle ID一定要跟你的项目中的info.plist中的Bundle ID保证一致!!!!
4. 以上代码中你需要修改的就是我在HelloWorldLayer.m类中的宏定义的Product ID(产品ID),例如Himi刚才新建了一个产品ID是“com.himi.wahaha"

然后我运行项目截图如下以及运行控制台打印的信息如下:
点击Buy之后运行截图以及打印信息:
输入测试账号密码后以及打印信息:

                害羞这里Himi最后一张截图是没有购买成功,这里Himi是故意截图出来的,原因就是想告诉童鞋们:
 如果你的产品信息能够正常得到,但是始终无法成功的话,不要着急,因为你的产品要进入iTunes Connect,并且Apple准备好沙箱环境需要一些时间。Himi之前遇到过,然后在过了段时间后我没有修改任何一行代码,但产品ID变为有效并能成功购买。=。 =郁闷ing~~ 其实要使产品发布到Apple的网络系统是需要一段时间的,so~这里别太着急!
           越狱机器无法正常测试沙盒的喔~
顺便提示一下:Bundle ID 尽可能与开发者证书的app ID 一致。
    好了,写了这么多了,咳咳、Himi继续忙了,做iOS的童鞋们我想此篇将成为你必须收藏的一篇哦~嘿嘿!