A. 新增 cocos2d Application Project: boardGame5
B. HelloWorldScene.h
// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
// HelloWorld Layer //@add
@interface HelloWorld : CCLayer <CCTargetedTouchDelegate>
// returns a Scene that contains the HelloWorld as the only child
+(id) scene;
@end
-----------------------------------------------------------------------------------------------
C. boardGame5AppDelegate.m
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
CC_DIRECTOR_INIT();
//@add
// Init the window
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
//@add
[window setUserInteractionEnabled:YES];
[window setMultipleTouchEnabled:YES];
....
}
-----------------------------------------------------------------------------------------------
D. HelloWorldScene.m
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init] )) {
//@add
self.isTouchEnabled = YES;
// @add
[[CCTouchDispatcher sharedDispatcher]
addTargetedDelegate:self priority:0 swallowsTouches:YES];
....
}
return self;
}
// @add
-(BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent*)event
{
CGPoint touchLocation=[touch locationInView:[touch view]];
touchLocation=[[CCDirector sharedDirector] convertToGL:touchLocation];
CGPoint nodePosition = [self convertToNodeSpace: touchLocation];
int t_x = nodePosition.x;
int t_y = nodePosition.y;
....
return YES;
}
2010年11月18日 星期四
2010年11月17日 星期三
iPhone 開發筆記17: cocos2d 圖片動畫效果
A. 新增 cocos2d Application Project.
B. 在 HelloWorldScene.m:
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init] )) {
....
// 從左上角往右下角切割圖檔
//CCSprite *sprite = [CCSprite spriteWithFile:@"Icon.png" rect:CGRectMake(20, 20, 57, 57)];
CCSprite *sprite = [CCSprite spriteWithFile:@"Icon.png"]; // source size
sprite.position = ccp(240, 50);
// (1). CCJumpTo: 跳躍至絕對座標
// [CCJumpTo actionWithDuration:2 position:ccp(300,0) height:50 jumps:4]; // -> 300, 0
// (2). CCJumpBy: 跳躍至相對座標(以原始座標累加距離的跳躍)
// [CCJumpBy actionWithDuration:2 position:ccp(300,0) height:50 jumps:4]; // -> 540, 50
// (3). CCRotateTo: 旋轉
// [CCRotateTo actionWithDuration:2 angle:180];
// (4). 淡入
// [CCFadeIn actionWithDuration:2];
// (5). 淡出
// [CCFadeOut actionWithDuration:2];
// (6). 移至絕對座標
// [CCMoveTo actionWithDuration:2 position: ccp(100, 100)];
// (7). 移至相對座標
// [CCMoveBy actionWithDuration:2 position: ccp(100, 100)]; // 340, 150
// 混合動作
// (8). CCSequence: one by one (連續動作)
[CCSequence actions:
// [CCRotateTo actionWithDuration:2 angle:180], // ---> error with reverse happend?
[CCRotateBy actionWithDuration:2 angle:180],
[CCJumpBy actionWithDuration:2 position:ccp(300,0) height:50 jumps:4],
[CCFadeOut actionWithDuration:2],
[CCCallFunc actionWithTarget:self selector:@selector(callback1)],
nil];
// 混合動作
// (9). CCSpawn: do it the sametime (同時動作)
/*
[CCSpawn actions:
[CCRotateTo actionWithDuration:2 angle:180], // ---> error with reverse happend?
[CCRotateBy actionWithDuration:2 angle:180],
[CCJumpBy actionWithDuration:2 position:ccp(300,0) height:50 jumps:4],
[CCFadeOut actionWithDuration:2],
[CCCallFunc actionWithTarget:self selector:@selector(callback1)],
nil];
*/
/* 動畫效果: case02 -> repeat */
// (10). CCRepeatForever: 無限重複執行
// CCRepeatForever *repeat = [CCRepeatForever actionWithAction:action];
// (11). CCRepeat: 重複執行
// CCRepeat *repeat = [CCRepeat actionWithAction:action times:2];
/* 動畫效果: case03 -> reverse */
// (12). reverse: 反向執行
// id t_reverse = [CCSequence actions:action, [action reverse], nil];
// runAction
// case 1: for normal
[sprite runAction:action];
// case 2: for repeat
// [sprite runAction:repeat];
// case 3: for reverse
// [sprite runAction:t_reverse];
[self addChild: sprite];
}
return self;
}
- (void) callback1
{
CCLabel *labelEnd = [CCLabel labelWithString:@"Game Over" fontName:@"Marker Felt" fontSize:64];
CGSize size = [[CCDirector sharedDirector] winSize];
// position the label on the center of the screen
labelEnd.position = ccp( size.width /2 , size.height/2 - 100 );
[self addChild: labelEnd];
}
B. 在 HelloWorldScene.m:
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init] )) {
....
// 從左上角往右下角切割圖檔
//CCSprite *sprite = [CCSprite spriteWithFile:@"Icon.png" rect:CGRectMake(20, 20, 57, 57)];
CCSprite *sprite = [CCSprite spriteWithFile:@"Icon.png"]; // source size
sprite.position = ccp(240, 50);
/* 動畫效果: case01 -> normal */
id action = // (1). CCJumpTo: 跳躍至絕對座標
// [CCJumpTo actionWithDuration:2 position:ccp(300,0) height:50 jumps:4]; // -> 300, 0
// (2). CCJumpBy: 跳躍至相對座標(以原始座標累加距離的跳躍)
// [CCJumpBy actionWithDuration:2 position:ccp(300,0) height:50 jumps:4]; // -> 540, 50
// (3). CCRotateTo: 旋轉
// [CCRotateTo actionWithDuration:2 angle:180];
// (4). 淡入
// [CCFadeIn actionWithDuration:2];
// (5). 淡出
// [CCFadeOut actionWithDuration:2];
// (6). 移至絕對座標
// [CCMoveTo actionWithDuration:2 position: ccp(100, 100)];
// (7). 移至相對座標
// [CCMoveBy actionWithDuration:2 position: ccp(100, 100)]; // 340, 150
// 混合動作
// (8). CCSequence: one by one (連續動作)
[CCSequence actions:
// [CCRotateTo actionWithDuration:2 angle:180], // ---> error with reverse happend?
[CCRotateBy actionWithDuration:2 angle:180],
[CCJumpBy actionWithDuration:2 position:ccp(300,0) height:50 jumps:4],
[CCFadeOut actionWithDuration:2],
[CCCallFunc actionWithTarget:self selector:@selector(callback1)],
nil];
// 混合動作
// (9). CCSpawn: do it the sametime (同時動作)
/*
[CCSpawn actions:
[CCRotateTo actionWithDuration:2 angle:180], // ---> error with reverse happend?
[CCRotateBy actionWithDuration:2 angle:180],
[CCJumpBy actionWithDuration:2 position:ccp(300,0) height:50 jumps:4],
[CCFadeOut actionWithDuration:2],
[CCCallFunc actionWithTarget:self selector:@selector(callback1)],
nil];
*/
/* 動畫效果: case02 -> repeat */
// (10). CCRepeatForever: 無限重複執行
// CCRepeatForever *repeat = [CCRepeatForever actionWithAction:action];
// (11). CCRepeat: 重複執行
// CCRepeat *repeat = [CCRepeat actionWithAction:action times:2];
/* 動畫效果: case03 -> reverse */
// (12). reverse: 反向執行
// id t_reverse = [CCSequence actions:action, [action reverse], nil];
// runAction
// case 1: for normal
[sprite runAction:action];
// case 2: for repeat
// [sprite runAction:repeat];
// case 3: for reverse
// [sprite runAction:t_reverse];
[self addChild: sprite];
}
return self;
}
- (void) callback1
{
CCLabel *labelEnd = [CCLabel labelWithString:@"Game Over" fontName:@"Marker Felt" fontSize:64];
CGSize size = [[CCDirector sharedDirector] winSize];
// position the label on the center of the screen
labelEnd.position = ccp( size.width /2 , size.height/2 - 100 );
[self addChild: labelEnd];
}
iPhone 開發筆記16: cocos2d 安裝與移除
A. cocos2d 官網: http://www.cocos2d-iphone.org/
B. 下載穩定版本(Stable version): http://www.cocos2d-iphone.org/download
C. 安裝方式:
1. 解壓縮下載的檔案: cocos2d-iphone-0.99.4.tar.gz
2. 在 Terminal 下, 到解壓縮目錄內執行:
$ cd cocos2d-iphone-0.99.4
$ sudo ./install-templates.sh
3. 可以看到以下的安裝訊息:
Installing cocos2d template:
/Library/Application Support/Developer/Shared/Xcode/Project Templates/cocos2d 0.99.4/cocos2d Application/
Installing cocos2d + box2d template:
/Library/Application Support/Developer/Shared/Xcode/Project Templates/cocos2d 0.99.4/cocos2d Box2d Application/
Installing cocos2d + chipmunk template:
/Library/Application Support/Developer/Shared/Xcode/Project Templates/cocos2d 0.99.4/cocos2d Chipmunk Application/
creating destination directory:
/Library/Application Support/Developer/Shared/Xcode/File Templates/cocos2d 0.99.4/
D. 移除方式:
刪除: C 安裝後所產生的目錄即可.
B. 下載穩定版本(Stable version): http://www.cocos2d-iphone.org/download
C. 安裝方式:
1. 解壓縮下載的檔案: cocos2d-iphone-0.99.4.tar.gz
2. 在 Terminal 下, 到解壓縮目錄內執行:
$ cd cocos2d-iphone-0.99.4
$ sudo ./install-templates.sh
3. 可以看到以下的安裝訊息:
Installing cocos2d template:
/Library/Application Support/Developer/Shared/Xcode/Project Templates/cocos2d 0.99.4/cocos2d Application/
Installing cocos2d + box2d template:
/Library/Application Support/Developer/Shared/Xcode/Project Templates/cocos2d 0.99.4/cocos2d Box2d Application/
Installing cocos2d + chipmunk template:
/Library/Application Support/Developer/Shared/Xcode/Project Templates/cocos2d 0.99.4/cocos2d Chipmunk Application/
creating destination directory:
/Library/Application Support/Developer/Shared/Xcode/File Templates/cocos2d 0.99.4/
D. 移除方式:
刪除: C 安裝後所產生的目錄即可.
iPhone 開發筆記15: 單點觸控與多點觸控
// 單點觸控
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
}
----------------------------------------------------------------------------------------------------
// 多點觸控
A. 新增一個 Window-based Application Project: multiTouch
B. 新增繼承 UIView 的 Class: TouchView
----------------------------------------------------------------------------------------------------
C. multiTouchAppDelegate.h
#import <UIKit/UIKit.h>
//@add
#import "TouchView.h"
@interface multiTouchAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
----------------------------------------------------------------------------------------------------
D. multiTouchAppDelegate.m
#import "multiTouchAppDelegate.h"
@implementation multiTouchAppDelegate
@synthesize window;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//@add
[application setStatusBarStyle:UIStatusBarStyleBlackOpaque]; // 改變狀態列背景
TouchView *touchView = [[[TouchView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
[window addSubview:touchView];
[window makeKeyAndVisible];
return YES;
}
----------------------------------------------------------------------------------------------------
E. TouchView.h
#import <UIKit/UIKit.h>
@interface TouchView : UIView {
//@add
CGPoint touch1;
CGPoint touch2;
double unitDistance;
double initialDistance;
}
//@add
- (double)getDistance:(CGPoint)fromPoint toPoint:(CGPoint)otherPoint;
@end
----------------------------------------------------------------------------------------------------
F. TouchView.m
#import "TouchView.h"
@implementation TouchView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
//@add
[self setMultipleTouchEnabled:YES];
}
return self;
}
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
NSArray *allTouches = [touches allObjects];
int count = [allTouches count];
if (count > 0)
touch1 = [[allTouches objectAtIndex:0] locationInView:self];
if (count > 1)
{
touch2 = [[allTouches objectAtIndex:1] locationInView:self];
initialDistance = [self getDistance:touch1 toPoint:touch2];
NSLog(@"initialDistance:%f",initialDistance);
}
[self setNeedsDisplay]; // 強制更新畫面: invoke -> drawRect
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray *allTouches = [touches allObjects];
int count = [allTouches count];
if (count > 0)
touch1 = [[allTouches objectAtIndex:0] locationInView:self];
if (count > 1)
{
touch2 = [[allTouches objectAtIndex:1] locationInView:self];
unitDistance = [self getDistance:touch1 toPoint:touch2];
NSLog(@"unitDistance:%f",unitDistance);
}
[self setNeedsDisplay]; // 強制更新畫面: invoke -> drawRect
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (initialDistance > unitDistance) {
// 縮小
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"multiTouch" message:@"Your touch is close to eachother" delegate:nil cancelButtonTitle:@"Yep, I did." otherButtonTitles:nil];
[alert show];
[alert release];
}
else if (initialDistance < unitDistance){
// 放大
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"multiTouch" message:@"Your touch is distant from eachother" delegate:nil cancelButtonTitle:@"Yep, I did." otherButtonTitles:nil];
[alert show];
[alert release];
}
}
// 二點距離: 平方相加, 開根號
- (double)getDistance:(CGPoint)fromPoint toPoint:(CGPoint)otherPoint
{
double deltaX = otherPoint.x - fromPoint.x;
double deltaY = otherPoint.y - fromPoint.y;
return sqrt(pow(deltaX, 2) + pow(deltaY, 2));
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
//@add Line
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextMoveToPoint(context,touch1.x,touch1.y);
CGContextAddLineToPoint(context,touch2.x,touch2.y);
CGContextStrokePath(context);
}
- (void)dealloc {
[super dealloc];
}
@end
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint location = [touch locationInView:touch.view];
// location.x
// location.y}
----------------------------------------------------------------------------------------------------
// 多點觸控
A. 新增一個 Window-based Application Project: multiTouch
B. 新增繼承 UIView 的 Class: TouchView
----------------------------------------------------------------------------------------------------
C. multiTouchAppDelegate.h
#import <UIKit/UIKit.h>
//@add
#import "TouchView.h"
@interface multiTouchAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
----------------------------------------------------------------------------------------------------
D. multiTouchAppDelegate.m
#import "multiTouchAppDelegate.h"
@implementation multiTouchAppDelegate
@synthesize window;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//@add
[application setStatusBarStyle:UIStatusBarStyleBlackOpaque]; // 改變狀態列背景
TouchView *touchView = [[[TouchView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
[window addSubview:touchView];
[window makeKeyAndVisible];
return YES;
}
----------------------------------------------------------------------------------------------------
E. TouchView.h
#import <UIKit/UIKit.h>
@interface TouchView : UIView {
//@add
CGPoint touch1;
CGPoint touch2;
double unitDistance;
double initialDistance;
}
//@add
- (double)getDistance:(CGPoint)fromPoint toPoint:(CGPoint)otherPoint;
@end
----------------------------------------------------------------------------------------------------
F. TouchView.m
#import "TouchView.h"
@implementation TouchView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
//@add
[self setMultipleTouchEnabled:YES];
}
return self;
}
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
NSArray *allTouches = [touches allObjects];
int count = [allTouches count];
if (count > 0)
touch1 = [[allTouches objectAtIndex:0] locationInView:self];
if (count > 1)
{
touch2 = [[allTouches objectAtIndex:1] locationInView:self];
initialDistance = [self getDistance:touch1 toPoint:touch2];
NSLog(@"initialDistance:%f",initialDistance);
}
[self setNeedsDisplay]; // 強制更新畫面: invoke -> drawRect
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray *allTouches = [touches allObjects];
int count = [allTouches count];
if (count > 0)
touch1 = [[allTouches objectAtIndex:0] locationInView:self];
if (count > 1)
{
touch2 = [[allTouches objectAtIndex:1] locationInView:self];
unitDistance = [self getDistance:touch1 toPoint:touch2];
NSLog(@"unitDistance:%f",unitDistance);
}
[self setNeedsDisplay]; // 強制更新畫面: invoke -> drawRect
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (initialDistance > unitDistance) {
// 縮小
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"multiTouch" message:@"Your touch is close to eachother" delegate:nil cancelButtonTitle:@"Yep, I did." otherButtonTitles:nil];
[alert show];
[alert release];
}
else if (initialDistance < unitDistance){
// 放大
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"multiTouch" message:@"Your touch is distant from eachother" delegate:nil cancelButtonTitle:@"Yep, I did." otherButtonTitles:nil];
[alert show];
[alert release];
}
}
// 二點距離: 平方相加, 開根號
- (double)getDistance:(CGPoint)fromPoint toPoint:(CGPoint)otherPoint
{
double deltaX = otherPoint.x - fromPoint.x;
double deltaY = otherPoint.y - fromPoint.y;
return sqrt(pow(deltaX, 2) + pow(deltaY, 2));
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
//@add Line
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextMoveToPoint(context,touch1.x,touch1.y);
CGContextAddLineToPoint(context,touch2.x,touch2.y);
CGContextStrokePath(context);
}
- (void)dealloc {
[super dealloc];
}
@end
2010年11月11日 星期四
iPhone 開發筆記14: 背景音樂
A. 在專案的 "Frameworks" 新增以下的 framework: CoreAudio.framework 與 AudioToolbox.framework .
B. 在專案的 "Groups & Files" 新增一個 Group: "Audio", 並新增以下的檔案: GBMusicTrack.h 與 GBMusicTrack.m
//
// GBMusicTrack.h
// GameBase
//
// Created by Jake Peterson (AnotherJake) on 7/6/08.
// Copyright 2008 Jake Peterson. All rights reserved.
//
//#import <Cocoa/Cocoa.h>
#import <AudioToolbox/AudioQueue.h>
#import <AudioToolbox/AudioFile.h>
#define NUM_QUEUE_BUFFERS 3
@interface GBMusicTrack : NSObject
{
AudioFileID audioFile;
AudioStreamBasicDescription dataFormat;
AudioQueueRef queue;
UInt64 packetIndex;
UInt32 numPacketsToRead;
AudioStreamPacketDescription *packetDescs;
BOOL repeat;
BOOL trackClosed;
AudioQueueBufferRef buffers[NUM_QUEUE_BUFFERS];
}
- (id)initWithPath:(NSString *)path;
- (void)setGain:(Float32)gain;
- (void)setRepeat:(BOOL)yn;
- (void)play;
- (void)pause;
// close is called automatically in GBMusicTrack's dealloc method, but it is recommended
// to call close first, so that the associated Audio Queue is released immediately, instead
// of having to wait for a possible autorelease, which may cause some conflict
- (void)close;
extern NSString *GBMusicTrackFinishedPlayingNotification;
@end
------------------------------------------------------------------------------------------------------
//
// GBMusicTrack.m
// GameBase
//
// Created by Jake Peterson (AnotherJake) on 7/6/08.
// Copyright 2008 Jake Peterson. All rights reserved.
//
#import "GBMusicTrack.h"
static UInt32 gBufferSizeBytes = 0x10000; // 64k
NSString *GBMusicTrackFinishedPlayingNotification
= @"GBMusicTrackFinishedPlayingNotification";
@interface GBMusicTrack (InternalMethods)
static void BufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef buffer);
- (void)callbackForBuffer:(AudioQueueBufferRef)buffer;
- (UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer;
@end
@implementation GBMusicTrack
#pragma mark -
#pragma mark GBMusicTrack
- (void)dealloc
{
[self close];
[super dealloc];
}
- (void)close
{
// it is preferrable to call close first, before dealloc if there is a problem waiting for
// an autorelease
if (trackClosed)
return;
trackClosed = YES;
AudioQueueStop(queue, YES);
AudioQueueDispose(queue, YES);
AudioFileClose(audioFile);
}
- (id)initWithPath:(NSString *)path
{
UInt32 size, maxPacketSize;
char *cookie;
int i;
if(!(self = [super init])) return nil;
if (path == nil) return nil;
// try to open up the file using the specified path
if (noErr != AudioFileOpenURL((CFURLRef)[NSURL fileURLWithPath:path], 0x01, kAudioFileCAFType, &audioFile))
{
NSLog(@"GBMusicTrack Error - initWithPath: could not open audio file. Path given was: %@", path);
return nil;
}
// get the data format of the file
size = sizeof(dataFormat);
AudioFileGetProperty(audioFile, kAudioFilePropertyDataFormat, &size, &dataFormat);
// create a new playback queue using the specified data format and buffer callback
AudioQueueNewOutput(&dataFormat, BufferCallback, self, nil, nil, 0, &queue);
// calculate number of packets to read and allocate space for packet descriptions if needed
if (dataFormat.mBytesPerPacket == 0 || dataFormat.mFramesPerPacket == 0)
{
// since we didn't get sizes to work with, then this must be VBR data (Variable BitRate), so
// we'll have to ask Core Audio to give us a conservative estimate of the largest packet we are
// likely to read with kAudioFilePropertyPacketSizeUpperBound
size = sizeof(maxPacketSize);
AudioFileGetProperty(audioFile, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
if (maxPacketSize > gBufferSizeBytes)
{
// hmm... well, we don't want to go over our buffer size, so we'll have to limit it I guess
maxPacketSize = gBufferSizeBytes;
NSLog(@"GBMusicTrack Warning - initWithPath: had to limit packet size requested for file path: %@", path);
}
numPacketsToRead = gBufferSizeBytes / maxPacketSize;
// will need a packet description for each packet since this is VBR data, so allocate space accordingly
packetDescs = malloc(sizeof(AudioStreamPacketDescription) * numPacketsToRead);
}
else
{
// for CBR data (Constant BitRate), we can simply fill each buffer with as many packets as will fit
numPacketsToRead = gBufferSizeBytes / dataFormat.mBytesPerPacket;
// don't need packet descriptsions for CBR data
packetDescs = nil;
}
// see if file uses a magic cookie (a magic cookie is meta data which some formats use)
AudioFileGetPropertyInfo(audioFile, kAudioFilePropertyMagicCookieData, &size, nil);
if (size > 0)
{
// copy the cookie data from the file into the audio queue
cookie = malloc(sizeof(char) * size);
AudioFileGetProperty(audioFile, kAudioFilePropertyMagicCookieData, &size, cookie);
AudioQueueSetProperty(queue, kAudioQueueProperty_MagicCookie, cookie, size);
free(cookie);
}
// allocate and prime buffers with some data
packetIndex = 0;
for (i = 0; i < NUM_QUEUE_BUFFERS; i++)
{
AudioQueueAllocateBuffer(queue, gBufferSizeBytes, &buffers[i]);
if ([self readPacketsIntoBuffer:buffers[i]] == 0)
{
// this might happen if the file was so short that it needed less buffers than we planned on using
break;
}
}
repeat = NO;
trackClosed = NO;
return self;
}
- (void)setGain:(Float32)gain
{
AudioQueueSetParameter(queue, kAudioQueueParam_Volume, gain);
}
- (void)setRepeat:(BOOL)yn
{
repeat = yn;
}
- (void)play
{
AudioQueueStart(queue, nil);
}
- (void)pause
{
AudioQueuePause(queue);
}
#pragma mark -
#pragma mark Callback
static void BufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef buffer)
{
// redirect back to the class to handle it there instead, so we have direct access to the instance variables
[(GBMusicTrack *)inUserData callbackForBuffer:buffer];
}
- (void)callbackForBuffer:(AudioQueueBufferRef)buffer
{
if ([self readPacketsIntoBuffer:buffer] == 0)
{
// End Of File reached, so rewind and refill the buffer using the beginning of the file instead
packetIndex = 0;
[self readPacketsIntoBuffer:buffer];
// if not repeating then we'll pause it so it's ready to play again immediately if needed
if (!repeat)
{
AudioQueuePause(queue);
// we're not in the main thread during this callback, so enqueue a message on the main thread to post notification
// that we're done, or else the notification will have to be handled in this thread, making things more difficult
[self performSelectorOnMainThread:@selector(postTrackFinishedPlayingNotification:) withObject:nil waitUntilDone:NO];
}
}
}
- (void)postTrackFinishedPlayingNotification:(id)object
{
// if we're here then we're in the main thread as specified by the callback, so now we can post notification that
// the track is done without the notification observer(s) having to worry about thread safety and autorelease pools
[[NSNotificationCenter defaultCenter] postNotificationName:GBMusicTrackFinishedPlayingNotification object:self];
}
- (UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer
{
UInt32 numBytes, numPackets;
// read packets into buffer from file
numPackets = numPacketsToRead;
AudioFileReadPackets(audioFile, NO, &numBytes, packetDescs, packetIndex, &numPackets, buffer->mAudioData);
if (numPackets > 0)
{
// - End Of File has not been reached yet since we read some packets, so enqueue the buffer we just read into
// the audio queue, to be played next
// - (packetDescs ? numPackets : 0) means that if there are packet descriptions (which are used only for Variable
// BitRate data (VBR)) we'll have to send one for each packet, otherwise zero
buffer->mAudioDataByteSize = numBytes;
AudioQueueEnqueueBuffer(queue, buffer, (packetDescs ? numPackets : 0), packetDescs);
// move ahead to be ready for next time we need to read from the file
packetIndex += numPackets;
}
return numPackets;
}
@end
------------------------------------------------------------------------------------------------------
C. 在 xxxViewController.m 檔:
#import "GBMusicTrack.h"
D. 在 xxxViewController.m 檔, 修改 viewDidLoad():
- (void)viewDidLoad {
[super viewDidLoad];
....
//background music
GBMusicTrack *song = [[GBMusicTrack alloc] initWithPath:[[NSBundle mainBundle] pathForResource:@"streetFighter" ofType:@"mp3"]];
[song setRepeat:YES];
[song play];
}
B. 在專案的 "Groups & Files" 新增一個 Group: "Audio", 並新增以下的檔案: GBMusicTrack.h 與 GBMusicTrack.m
//
// GBMusicTrack.h
// GameBase
//
// Created by Jake Peterson (AnotherJake) on 7/6/08.
// Copyright 2008 Jake Peterson. All rights reserved.
//
//#import <Cocoa/Cocoa.h>
#import <AudioToolbox/AudioQueue.h>
#import <AudioToolbox/AudioFile.h>
#define NUM_QUEUE_BUFFERS 3
@interface GBMusicTrack : NSObject
{
AudioFileID audioFile;
AudioStreamBasicDescription dataFormat;
AudioQueueRef queue;
UInt64 packetIndex;
UInt32 numPacketsToRead;
AudioStreamPacketDescription *packetDescs;
BOOL repeat;
BOOL trackClosed;
AudioQueueBufferRef buffers[NUM_QUEUE_BUFFERS];
}
- (id)initWithPath:(NSString *)path;
- (void)setGain:(Float32)gain;
- (void)setRepeat:(BOOL)yn;
- (void)play;
- (void)pause;
// close is called automatically in GBMusicTrack's dealloc method, but it is recommended
// to call close first, so that the associated Audio Queue is released immediately, instead
// of having to wait for a possible autorelease, which may cause some conflict
- (void)close;
extern NSString *GBMusicTrackFinishedPlayingNotification;
@end
------------------------------------------------------------------------------------------------------
//
// GBMusicTrack.m
// GameBase
//
// Created by Jake Peterson (AnotherJake) on 7/6/08.
// Copyright 2008 Jake Peterson. All rights reserved.
//
#import "GBMusicTrack.h"
static UInt32 gBufferSizeBytes = 0x10000; // 64k
NSString *GBMusicTrackFinishedPlayingNotification
= @"GBMusicTrackFinishedPlayingNotification";
@interface GBMusicTrack (InternalMethods)
static void BufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef buffer);
- (void)callbackForBuffer:(AudioQueueBufferRef)buffer;
- (UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer;
@end
@implementation GBMusicTrack
#pragma mark -
#pragma mark GBMusicTrack
- (void)dealloc
{
[self close];
[super dealloc];
}
- (void)close
{
// it is preferrable to call close first, before dealloc if there is a problem waiting for
// an autorelease
if (trackClosed)
return;
trackClosed = YES;
AudioQueueStop(queue, YES);
AudioQueueDispose(queue, YES);
AudioFileClose(audioFile);
}
- (id)initWithPath:(NSString *)path
{
UInt32 size, maxPacketSize;
char *cookie;
int i;
if(!(self = [super init])) return nil;
if (path == nil) return nil;
// try to open up the file using the specified path
if (noErr != AudioFileOpenURL((CFURLRef)[NSURL fileURLWithPath:path], 0x01, kAudioFileCAFType, &audioFile))
{
NSLog(@"GBMusicTrack Error - initWithPath: could not open audio file. Path given was: %@", path);
return nil;
}
// get the data format of the file
size = sizeof(dataFormat);
AudioFileGetProperty(audioFile, kAudioFilePropertyDataFormat, &size, &dataFormat);
// create a new playback queue using the specified data format and buffer callback
AudioQueueNewOutput(&dataFormat, BufferCallback, self, nil, nil, 0, &queue);
// calculate number of packets to read and allocate space for packet descriptions if needed
if (dataFormat.mBytesPerPacket == 0 || dataFormat.mFramesPerPacket == 0)
{
// since we didn't get sizes to work with, then this must be VBR data (Variable BitRate), so
// we'll have to ask Core Audio to give us a conservative estimate of the largest packet we are
// likely to read with kAudioFilePropertyPacketSizeUpperBound
size = sizeof(maxPacketSize);
AudioFileGetProperty(audioFile, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
if (maxPacketSize > gBufferSizeBytes)
{
// hmm... well, we don't want to go over our buffer size, so we'll have to limit it I guess
maxPacketSize = gBufferSizeBytes;
NSLog(@"GBMusicTrack Warning - initWithPath: had to limit packet size requested for file path: %@", path);
}
numPacketsToRead = gBufferSizeBytes / maxPacketSize;
// will need a packet description for each packet since this is VBR data, so allocate space accordingly
packetDescs = malloc(sizeof(AudioStreamPacketDescription) * numPacketsToRead);
}
else
{
// for CBR data (Constant BitRate), we can simply fill each buffer with as many packets as will fit
numPacketsToRead = gBufferSizeBytes / dataFormat.mBytesPerPacket;
// don't need packet descriptsions for CBR data
packetDescs = nil;
}
// see if file uses a magic cookie (a magic cookie is meta data which some formats use)
AudioFileGetPropertyInfo(audioFile, kAudioFilePropertyMagicCookieData, &size, nil);
if (size > 0)
{
// copy the cookie data from the file into the audio queue
cookie = malloc(sizeof(char) * size);
AudioFileGetProperty(audioFile, kAudioFilePropertyMagicCookieData, &size, cookie);
AudioQueueSetProperty(queue, kAudioQueueProperty_MagicCookie, cookie, size);
free(cookie);
}
// allocate and prime buffers with some data
packetIndex = 0;
for (i = 0; i < NUM_QUEUE_BUFFERS; i++)
{
AudioQueueAllocateBuffer(queue, gBufferSizeBytes, &buffers[i]);
if ([self readPacketsIntoBuffer:buffers[i]] == 0)
{
// this might happen if the file was so short that it needed less buffers than we planned on using
break;
}
}
repeat = NO;
trackClosed = NO;
return self;
}
- (void)setGain:(Float32)gain
{
AudioQueueSetParameter(queue, kAudioQueueParam_Volume, gain);
}
- (void)setRepeat:(BOOL)yn
{
repeat = yn;
}
- (void)play
{
AudioQueueStart(queue, nil);
}
- (void)pause
{
AudioQueuePause(queue);
}
#pragma mark -
#pragma mark Callback
static void BufferCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef buffer)
{
// redirect back to the class to handle it there instead, so we have direct access to the instance variables
[(GBMusicTrack *)inUserData callbackForBuffer:buffer];
}
- (void)callbackForBuffer:(AudioQueueBufferRef)buffer
{
if ([self readPacketsIntoBuffer:buffer] == 0)
{
// End Of File reached, so rewind and refill the buffer using the beginning of the file instead
packetIndex = 0;
[self readPacketsIntoBuffer:buffer];
// if not repeating then we'll pause it so it's ready to play again immediately if needed
if (!repeat)
{
AudioQueuePause(queue);
// we're not in the main thread during this callback, so enqueue a message on the main thread to post notification
// that we're done, or else the notification will have to be handled in this thread, making things more difficult
[self performSelectorOnMainThread:@selector(postTrackFinishedPlayingNotification:) withObject:nil waitUntilDone:NO];
}
}
}
- (void)postTrackFinishedPlayingNotification:(id)object
{
// if we're here then we're in the main thread as specified by the callback, so now we can post notification that
// the track is done without the notification observer(s) having to worry about thread safety and autorelease pools
[[NSNotificationCenter defaultCenter] postNotificationName:GBMusicTrackFinishedPlayingNotification object:self];
}
- (UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer
{
UInt32 numBytes, numPackets;
// read packets into buffer from file
numPackets = numPacketsToRead;
AudioFileReadPackets(audioFile, NO, &numBytes, packetDescs, packetIndex, &numPackets, buffer->mAudioData);
if (numPackets > 0)
{
// - End Of File has not been reached yet since we read some packets, so enqueue the buffer we just read into
// the audio queue, to be played next
// - (packetDescs ? numPackets : 0) means that if there are packet descriptions (which are used only for Variable
// BitRate data (VBR)) we'll have to send one for each packet, otherwise zero
buffer->mAudioDataByteSize = numBytes;
AudioQueueEnqueueBuffer(queue, buffer, (packetDescs ? numPackets : 0), packetDescs);
// move ahead to be ready for next time we need to read from the file
packetIndex += numPackets;
}
return numPackets;
}
@end
------------------------------------------------------------------------------------------------------
C. 在 xxxViewController.m 檔:
#import "GBMusicTrack.h"
D. 在 xxxViewController.m 檔, 修改 viewDidLoad():
- (void)viewDidLoad {
[super viewDidLoad];
....
//background music
GBMusicTrack *song = [[GBMusicTrack alloc] initWithPath:[[NSBundle mainBundle] pathForResource:@"streetFighter" ofType:@"mp3"]];
[song setRepeat:YES];
[song play];
}
2010年11月10日 星期三
iPhone 開發筆記13: 單一圖片與動態切換圖片
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
// single image // x, y, width, height
UIImageView *singleImage = [[UIImageView alloc] initWithFrame:CGRectMake(100, 125, 150, 130)];
singleImage.image = [UIImage imageNamed:@"1.png"];
singleImage.contentMode = UIViewContentModeBottomLeft; // 置於 frame 的左下角
singleImage.alpha = 0.5f; // 透明度
[self.view addSubview:singleImage];
-----------------------------------------------------------------------------------------------------
// dynamic images
NSArray * imageArray = [[NSArray alloc] initWithObjects:
[UIImage imageNamed:@"1.png"],
[UIImage imageNamed:@"2.png"],
[UIImage imageNamed:@"3.png"],
[UIImage imageNamed:@"4.png"],
[UIImage imageNamed:@"5.png"],
[UIImage imageNamed:@"6.png"],
[UIImage imageNamed:@"7.png"],
[UIImage imageNamed:@"8.png"],
[UIImage imageNamed:@"9.png"],
[UIImage imageNamed:@"10.png"],
[UIImage imageNamed:@"11.png"],
[UIImage imageNamed:@"12.png"],
nil];
// x, y, width, height
UIImageView *dynamicImages = [[UIImageView alloc] initWithFrame:CGRectMake(100, 125, 150, 130)];
dynamicImages.animationImages = imageArray;
dynamicImages.animationDuration = 1.0f;
dynamicImages.contentMode = UIViewContentModeBottomLeft; // 置於 frame 的左下角
[self.view addSubview:dynamicImages];
[dynamicImages startAnimating];
}
- (void)viewDidLoad {
[super viewDidLoad];
// single image // x, y, width, height
UIImageView *singleImage = [[UIImageView alloc] initWithFrame:CGRectMake(100, 125, 150, 130)];
singleImage.image = [UIImage imageNamed:@"1.png"];
singleImage.contentMode = UIViewContentModeBottomLeft; // 置於 frame 的左下角
singleImage.alpha = 0.5f; // 透明度
[self.view addSubview:singleImage];
-----------------------------------------------------------------------------------------------------
// dynamic images
NSArray * imageArray = [[NSArray alloc] initWithObjects:
[UIImage imageNamed:@"1.png"],
[UIImage imageNamed:@"2.png"],
[UIImage imageNamed:@"3.png"],
[UIImage imageNamed:@"4.png"],
[UIImage imageNamed:@"5.png"],
[UIImage imageNamed:@"6.png"],
[UIImage imageNamed:@"7.png"],
[UIImage imageNamed:@"8.png"],
[UIImage imageNamed:@"9.png"],
[UIImage imageNamed:@"10.png"],
[UIImage imageNamed:@"11.png"],
[UIImage imageNamed:@"12.png"],
nil];
// x, y, width, height
UIImageView *dynamicImages = [[UIImageView alloc] initWithFrame:CGRectMake(100, 125, 150, 130)];
dynamicImages.animationImages = imageArray;
dynamicImages.animationDuration = 1.0f;
dynamicImages.contentMode = UIViewContentModeBottomLeft; // 置於 frame 的左下角
[self.view addSubview:dynamicImages];
[dynamicImages startAnimating];
}
iPhone 開發筆記12: accelerometer 加速計
A. 在 xxx project 的 .h 檔 Class 宣告裡, 新增協定: <UIAccelerometerDelegate>
B. 在 xxx project 的 .m 檔的 Class 定義裡:
// Constant for the number of times per second (Hertz) to acceleration.
#define kAccelerometerFrequency 50
....
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
....
// Configure and start the accelerometer
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)];
[[UIAccelerometer sharedAccelerometer] setDelegate:self]; // .h => : <UIAccelerometerDelegate>
}
// UIAccelerometerDelegate method, called when the device accelerates.
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
CGRect greenFrame = racquet_green.frame;
greenFrame.origin.x = greenFrame.origin.x + acceleration.x;
racquet_green.frame = greenFrame;
/*
NSLog(@"accelerometer");
NSLog(@"x:%f",acceleration.x); // -1 ~ 1
NSLog(@"y:%f",acceleration.y); // -1 ~ 1
NSLog(@"z:%f",acceleration.z);
*/
}
B. 在 xxx project 的 .m 檔的 Class 定義裡:
// Constant for the number of times per second (Hertz) to acceleration.
#define kAccelerometerFrequency 50
....
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
....
// Configure and start the accelerometer
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)];
[[UIAccelerometer sharedAccelerometer] setDelegate:self]; // .h => : <UIAccelerometerDelegate>
}
// UIAccelerometerDelegate method, called when the device accelerates.
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
CGRect greenFrame = racquet_green.frame;
greenFrame.origin.x = greenFrame.origin.x + acceleration.x;
racquet_green.frame = greenFrame;
/*
NSLog(@"accelerometer");
NSLog(@"x:%f",acceleration.x); // -1 ~ 1
NSLog(@"y:%f",acceleration.y); // -1 ~ 1
NSLog(@"z:%f",acceleration.z);
*/
}
iPhone 開發筆記11: 橫向畫面設定
A. 新增一個 View-based Project.
B. 在 Resources 裡, 新增一個背景圖檔(ex: background.jpg).
C. 在 Interface Builder 的 View 裡, 新增一個 UIImageView, 並設定剛剛的背景圖檔.
------------------------------------------------------------------------------------------------------
D. 在 Interface Builder 裡, 點選 View , 在 View Attributes 的 Simulated User Interface Elements 下的 Orientation 選擇: Landscape (風景畫: 代表橫向).
------------------------------------------------------------------------------------------------------
E. 打開 xxx-info.plist 檔, 新增以下一組 Key 與 Value:
Key: Initial interface orientation
Value: Landscape(right home button)
------------------------------------------------------------------------------------------------------
F.在程式裡, 加上一段 code:
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
//return (interfaceOrientation == UIInterfaceOrientationPortrait);
//@add
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
B. 在 Resources 裡, 新增一個背景圖檔(ex: background.jpg).
C. 在 Interface Builder 的 View 裡, 新增一個 UIImageView, 並設定剛剛的背景圖檔.
------------------------------------------------------------------------------------------------------
D. 在 Interface Builder 裡, 點選 View , 在 View Attributes 的 Simulated User Interface Elements 下的 Orientation 選擇: Landscape (風景畫: 代表橫向).
------------------------------------------------------------------------------------------------------
E. 打開 xxx-info.plist 檔, 新增以下一組 Key 與 Value:
Key: Initial interface orientation
Value: Landscape(right home button)
------------------------------------------------------------------------------------------------------
F.在程式裡, 加上一段 code:
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
//return (interfaceOrientation == UIInterfaceOrientationPortrait);
//@add
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
iPhone 開發筆記10: 利用 UIWebView 讀取 PDF 檔案
A. 新增一個 View-based Project(ex: viewPDF).
B. 在 Resources 裡, 新增一個 PDF 檔(ex: top.pdf).
C. 在 Interface Builder 的 View 裡, 新增一個 UIWebView.
-----------------------------------------------------------------------------------------------------------------
D. 調整 viewPDFViewController.h 如下:
#import <UIKit/UIKit.h>
@interface viewPDFViewController : UIViewController {
UIWebView *myUIWebView;
}
@property (nonatomic, retain) IBOutlet UIWebView *myUIWebView;
@end
-----------------------------------------------------------------------------------------------------------------
E. 調整 viewPDFViewController.m 如下:
#import "viewPDFViewController.h"
@implementation viewPDFViewController
@synthesize myUIWebView;
........
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
NSString *pdfPath = [[NSBundle mainBundle] pathForResource:@"toc" ofType:@"pdf"];
NSURL *pdfURL = [NSURL fileURLWithPath:pdfPath];
NSURLRequest *pdfRequest = [NSURLRequest requestWithURL:pdfURL];
[myUIWebView loadRequest:pdfRequest];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.myUIWebView = nil;
}
- (void)dealloc {
[myUIWebView release];
[super dealloc];
}
-----------------------------------------------------------------------------------------------------------------
F. 在 Interface Builder , 將 File's Owner 的 Outlets: myUIWebView 連結到 View 下的 UIWebView.
B. 在 Resources 裡, 新增一個 PDF 檔(ex: top.pdf).
C. 在 Interface Builder 的 View 裡, 新增一個 UIWebView.
-----------------------------------------------------------------------------------------------------------------
D. 調整 viewPDFViewController.h 如下:
#import <UIKit/UIKit.h>
@interface viewPDFViewController : UIViewController {
UIWebView *myUIWebView;
}
@property (nonatomic, retain) IBOutlet UIWebView *myUIWebView;
@end
-----------------------------------------------------------------------------------------------------------------
E. 調整 viewPDFViewController.m 如下:
#import "viewPDFViewController.h"
@implementation viewPDFViewController
@synthesize myUIWebView;
........
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
NSString *pdfPath = [[NSBundle mainBundle] pathForResource:@"toc" ofType:@"pdf"];
NSURL *pdfURL = [NSURL fileURLWithPath:pdfPath];
NSURLRequest *pdfRequest = [NSURLRequest requestWithURL:pdfURL];
[myUIWebView loadRequest:pdfRequest];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.myUIWebView = nil;
}
- (void)dealloc {
[myUIWebView release];
[super dealloc];
}
-----------------------------------------------------------------------------------------------------------------
F. 在 Interface Builder , 將 File's Owner 的 Outlets: myUIWebView 連結到 View 下的 UIWebView.
iPhone 開發筆記09: 小記
A. 在 iPhone 模擬器內, 使用者可以存放檔案的目錄位置:
lanli(使用者) > library(資源庫) > Application Support > iPhone Simulator > 4.1 > Applications > 05B3668E-9938-4220-BEC5-BBF27900992D (編碼的目錄) > Ducuments
lanli(使用者) > library(資源庫) > Application Support > iPhone Simulator > 4.1 > Applications > 05B3668E-9938-4220-BEC5-BBF27900992D (編碼的目錄) > Ducuments
訂閱:
文章 (Atom)