2010年11月18日 星期四

iPhone 開發筆記18: cocos2d MultipleTouch

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月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);
   
        /* 動畫效果: 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 安裝後所產生的目錄即可.

iPhone 開發筆記15: 單點觸控與多點觸控

// 單點觸控
- (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.frameworkAudioToolbox.framework .

B. 在專案的 "Groups & Files" 新增一個 Group: "Audio", 並新增以下的檔案: GBMusicTrack.hGBMusicTrack.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];
}

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);
     */
}

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);
}

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.

iPhone 開發筆記09: 小記

A. 在 iPhone 模擬器內, 使用者可以存放檔案的目錄位置:
lanli(使用者) > library(資源庫) > Application Support > iPhone Simulator > 4.1 > Applications > 05B3668E-9938-4220-BEC5-BBF27900992D (編碼的目錄) > Ducuments