2011年11月30日 星期三

iOS - Training (9) - UINavigationController



方便切換UIViewController。為一個stack型態的容器,使用者視角從stack的入口看下去,所以後來放入的view會蓋過舊的view而被使用者看到,相對的回上一個view時,上層的view會被取消掉而不存在:
使用範例:
新增一個繼承UIViewController的類別Controller_A,連同Xib一起產生;同樣的也新增一個Controller_B。在A的Xib中拉入button,定義button的action
#import "Controller_B.h"

- (IBAction)callController:(id)sender {

    Controller_B* conB = [[Controller_B alloc] initWithNibName:@"Controller_B" bundle:nil];
    
    [self.navigationController pushViewController:conB animated:YES]; // UIViewController中有navigationController這個property,用來將Controller放入UINavigationController的stack中

//    [conB release]; // If no ARC
}
先在Delega.h中加入UINavigationController
@interface AppDelegate : UIResponder <UIApplicationDelegate>

{
    UINavigationController* navController;
}
再定義Delega.m
#import "Controller_A.h"

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

{
    // Override point for customization after application launch.
    Controller_A* conA = [[Controller_A alloc] initWithNibName:@"Controller_A" bundle:nil];
    navController = [[UINavigationController alloc] initWithRootViewController:conA]; //初始化時將conA物件放入navController

    self.window.rootViewController = navController; //將navController放入主畫面中
    [self.window makeKeyAndVisible];
    
    return YES;
}

自訂UINavigationBar樣式 -----
在採用UINavigateController時,畫面上方會出現條bar,可以自行定義bar裡的內容,分別有leftBarButtonItem、titleView、rightBarButtonItem

titleView:
在UIViewController之中提供navigationItem.title property來設定title
- (void)viewDidLoad

{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    self.navigationItem.title = @"Controller A";
}

leftBarButtonItem或rightBarButtonItem:
使用UIBarButtonItem,並將navigationItem.rightBarButtonItem指向UIBarButtonItem
- (void)viewDidLoad

{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    self.navigationItem.title = @"Controller A";
    
    UIBarButtonItem* rightBtn = [[UIBarButtonItem alloc] initWithTitle:@"About" style:UIBarButtonItemStylePlain target:self action:@selector(about:)]; //style參數決定按鈕的樣式,target與action設定呼叫的方法
    
    self.navigationItem.rightBarButtonItem = rightBtn; //將rightBtn指向navigationItem.rightBarButtonItem property
}

-(void) about:(id) sender
{
    NSLog(@"Hello I am Controller A");
}

改變back button:
同樣使用UIBarButtonItem來改變原有的back button
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    self.navigationItem.title = @"Controller A";
    
    UIBarButtonItem* rightBtn = [[UIBarButtonItem alloc] initWithTitle:@"About" style:UIBarButtonItemStylePlain target:self action:@selector(about:)]; //style參數決定按鈕的樣式,target與action設定呼叫的方法
    
    self.navigationItem.rightBarButtonItem = rightBtn; //將rightBtn指向navigationItem.rightBarButtonItem property

    UIBarButtonItem* backBtn = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStylePlain target:nil action:nil]; //style和target為UINavigationController控制,故就算給予定義也不會有反應

    
    self.navigationItem.backBarButtonItem = backBtn;
}

-(void) about:(id) sender
{
    NSLog(@"Hello I am Controller A");
}
-----

資料出處《Object-C與iOS開發入門》

2011年11月29日 星期二

iOS - Training (8) - Data Access

iOS App目錄結構 -----
<APP_HOME>:iOS為了保護APP而在每次開啓APP時會給予流水號。
<APP_HOME>/AppName.app:主程式碼,亦稱bundle,不被iTunes備份。
<APP_HOME>/Documents:資料永久存放地,會備份。
<APP_HOME>/Library:非使用者資料相關,會備份。
<APP_HOME>/Library/Preferences:透過NSUserDefaults存放的程式相關設定檔(同Android的SharedPreference),會備份。
<APP_HOME>/Library/Caches:更新時暫存資料區,不備份。
<APP_HOME>/tmp:暫時性資料,不備份。
-----


在Xcode中,黃色的資料夾代表group,表示非實際存在的資料夾。而藍色的資料夾才代表實際存在檔案系統中的資料夾。


取得檔案路徑 -----
取出指定檔案的路徑:
// pathForResouce為檔名,ofType為檔案型態
[[NSBundle mainBundle] pathForResource:@"icon" ofType:@"png"];
加上指定資料夾取出路徑:
[[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/test/icon.png];
或:
// inDirectory為資料夾名稱
[[NSBundle mainBundle] pathForResource:@"icon" ofType:@"png" inDirectory:@"test"];

取<APP_HOME>/Documents 內的檔案路徑(simulator與實機取出的路徑不相同):
NSArray* path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];

取<APP_HOME>/tmp:
NSTemporaryDirectory();
-----
<APP_HOME>/Library/Preferences 內資料存取 -----
    在New File中,開啓Setting Bundle,會出現相關的.plist,裡頭可以增減設定的參數。在程式中呼叫的話是以identifier為key來取得其中的參數,所以identifier不能重覆。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    [[NSUserDefaults standardUserDefaults] setValue:@"Michael" forKey:@"name_preference"]; //將Michael這個值放入key為name_preference的item中
    [[NSUserDefaults standardUserDefaults] synchronize]; //確定放入,同Android中的SharedPreference的commit方法
    NSLog(@"%@", [[NSUserDefaults standardUserDefaults] stringForKey:@"name_preference"]);
    
    return YES;
}
-----
Property List存取 -----
    若要存的文字檔並不是很大(不超過1MB,通常用來存取遊戲中的點數)時,將內容存在Property List中。
開啟繼承UIViewController的類別
- (void)viewDidLoad
{
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    // 將內容存入Array中,記得最後需加上nil表示array結束
    NSArray* myData = [NSArray arrayWithObjects:@"first", @"second", @"third", nil];

    // 將Document的路徑取出
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* documentsDirectory = [paths objectAtIndex:0];

    // 加上檔案名稱。stringByAppendingPathComponent方法是將後面的參數加上"/",好處是如果今天是window系統時,會自動換成"\"
    NSString* filePath = [documentsDirectory stringByAppendingPathComponent:@"data.plist"];

    // 寫入檔案
    [myData writeToFile:filePath atomically:YES];
}
加入一個button並寫入動作
- (IBAction)readProperty:(id)sender {
    // 將Document的路徑取出
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* docDirectory = [paths objectAtIndex:0];

    // 讀出檔案內資料
    NSString* filePath = [docDirectory stringByAppendingPathComponent:@"data.plist"];
    NSArray* dataArray = [NSArray arrayWithContentsOfFile:filePath];
    
    NSLog(@"%@", dataArray);
}
-----

資料出處《Object-C與iOS開發入門》

2011年11月28日 星期一

iOS - Training (7) - Controller

Controller-UI 連結 -----
在iOS中為了連結Controller與UI之間的關係,可以透過

  • Xib裡的File's Owner來連結二者
  • 直接在Controller中撰寫程式來實現介面。

Xib裡的File's Owner來連結二者
    開啓一個繼承UIViewController的類別,並也產生一個Xib檔(可由IDE勾選一同產生)。點選Xib檔並選擇Placeholders裡的File's Owner,在右邊的Identity Inspector中的Custom Class裡設定與之連結的Class,這裡選擇剛開啓的UIViewController類別,而完成上述的Controller-UI連結(若File's Owner的Connection Inspector裡的Outlets沒與Placeholder中的Object裡的view作連結,需在這裡自行連結)。
    上述只完成了Controller-UI二者的連結,但主畫面並無與此view作連結。所以需在Delegate中的didFinishLaunchingWithOptions方法實體化Controller。
#import "MyController.h" //需import繼承UIViewController的類別

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    MyController* myCon = [[MyController alloc] initWithNibName:@"MyController" bundle:nil];

    self.window.rootViewController = myCon; //將Controller放入主畫面中
    //[myCon release]; //沒使用ARC機制時需自行釋放記憶體


    [self.window makeKeyAndVisible];
    
    return YES;
}

直接在Controller中撰寫程式來實現介面
    開啓一個繼承UIViewController的類別,但這裡不需產生Xib檔,而是覆寫UIViewController中的loadView方法。
-(void)loadView
{
    UIView* myView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 480.0)];
    UILabel* myLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, 50, 120, 40)];
    myLabel.text = @"Hello View";
    [myView addSubview:myLabel];
//    [myLabel release];
    self.view = myView;
//    [myView release];
}
再來在主畫面中實體化Controller

#import "MyController.h" //需import繼承UIViewController的類別

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    MyController* myCon = [[MyController alloc] init]; //因為由程式來實現介面,故不用加入Xib檔名
    self.window.rootViewController = myCon;
//    [myCon release];
    [self.window makeKeyAndVisible];
    
    return YES;
}
-----
UIViewController 生命週期 -----
-----
UIView 事件傳遞-----
故當A_UIView蓋在B_UIView上時,若A_UIView的Inspector裡的View設定項中的Interaction "User Interaction Enabled"打勾,表示此UIView會被使用者事件觸發,B_UIView不會被使用者事件所觸發;若A_UIView取消打勾,B_UIView同樣選項有打勾的話,B_UIView就會觸發事件。
-----

資料出處《Object-C與iOS開發入門》

2011年11月27日 星期日

iOS - Training (6) - UI

與Android相同,iOS一切view的源頭為UIView。

設定 -----
開啓storyboard視窗後,開啓右邊的inspector視窗,可以針對view作設定:

  • 基本大小色彩等初始化設定
  • 與superview作等比例大小自動切換設定
  • 本體等比例大小自動切換設定
  • 自動大小切換時的重心座標設定

-----
繪圖 -----
原則:
  1. 定義路徑(想要繪畫的view)。線寬、位移、旋轉。
  2. 設定顏色
  3. 填色
  4. 回到1.或結束
Example_畫方格:
  • New一個繼承UIView的物件,並重寫drawRect(與 Android的onDraw相同)
-(void) drawRect:(CGRect)rect
{
    CGRect myRect = CGRectMake(10, 10, 60, 80); //定義路徑
    [[UIColor cyanColor] set]; //設定填滿顏色
    UIRectFile(myRect); //填滿
    [[UIColor blackColor] set]; //設定框架顏色
    UIRectFrame(myRect); //畫框
}

Example_幾何圖:
  • 在storyboard中,拉入一個UIView和Button
  • 將Button action "Touch Up Inside"與自行定義的方法moveObject作連結
-(IBAction)moveObject:(id)sender
{

   CGPoint originalCenter = targetView.center;
   originalCenter = CGPointMake(originalCenter.x + 100, originalCenter.y);
   [UIView beginAnimations:@"Test move" context:NULL];
   [UIView setAnimationDuration:0.5];
   [UIView setAnimationDelegate:self];
   [UIView setAnimationDidStopSelector:@selector(animStopped:)];
   targetView.center = originalCenter;
   [UIView commitAnimations];
}
*另外寫法 (iOS 4)
-(IBAction)moveObject:(id)sender
{

   CGPoint originalCenter = targetView.center;
   originalCenter = CGPointMake(originalCenter.x + 100, originalCenter.y);
   [UIView animateWithDuration:0.5 animations:
     ^(void){
         targetView.center = originalCenter;
     }completion:^(BOOL finished){
         NSLog(@"Stop animation");
     }];
}

若要繪製更多變的圖形除了可以使用CGContext,另外還有CGPath:
  • CGContext系列物件:使用上較為簡單。
  • CGPath系列物件:會將繪製的圖存成CGMutablePathRef,以便再利用而不需重新定義路徑、顏色、上色等。
若要讓view重新再繪製,如同Android並不是直接呼叫onDraw而是藉由invalidate()來重繪,在iOS中呼叫setNeedsDisplay達到重繪的動作。
-----
插圖 -----
插圖的方法與Android中的ImageView相同,iOS同樣有個UIImageView物件

可以在storyboard或程式碼中設定:

  • 在storyboard中拉進Image view並在右方的Inspector視窗中設定圖片來源
(graph)
  • 程式碼設定
    UIImage* myImage = [UIImage imageName:@"testimage.png"]; //指定圖片路徑
    UIImageView* myImageView = [[UIImageView alloc] initWithImage:myImage];
    myImageView.frame = CGRectMake(0, 0, 240, 240);
    [window addSubview:myImageView];
-----
介面方向 -----
Device各方向常數:
  • UIInterfaceOrientationPortrait:實體按鈕在下
  • UIInterfaceOrientationUpsideDown:實體按鈕在上
  • UIInterfaceOrientationLandscapeLeft:實體按鈕在左
  • UIInterfaceOrientationLandscapeRight:實體按鈕在右
決定Device翻轉的方向
// interfaceOrientation參數為Device目前的方向
// 當return值為true時,改變畫面方向
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return YES; //當Device翻轉時即翻轉方向

    //return (UIInterfaceOrientationPortrait == interfaceOrientation)); //判斷Device是不是呈垂直狀態,是的話切換畫面,讓畫面固定在直放的狀態

    //return (UIInterfaceOrientationIsLandscape(interfaceOrientation); //使用UIInterfaceOrienttionIsLandscape方法來判斷目前是不是橫放的狀態,是的話切換畫面,讓畫面固定在橫放的狀態
}
-----

資料出處《Object-C與iOS開發入門》

2011年11月22日 星期二

iOS - Training (5) - Taget Action Event concept

Target:定義Action的物件
Action:執行Event的方法
Event:使用者觸發的動作

Event產生後,由Target所實作的Action來動作。
-----
iOS裡將元件與其動作作關聯的方法,是透過托拉的方式。
按著option鈕,選定要關聯的.h檔,接下來可以使用下列其中一項來完成關聯:

  • (建議)選定元件對映的Event,托拉到.h檔裡
(graph)
然後名命此Action及其參數
(graph)
  • 先定義好Action後,將元件內欲對映的Event托拉到定義好的Action上
(graph)
-----

資料出處《Object-C與iOS開發入門》

iOS - Training (4) - 文件基本架構

各資料夾內容 -----
(Product Name)
  ﹣程式碼、與UI有關的xib(storyboard)檔。
Supporting Files
  ﹣main、設定檔及圖片音樂檔案。
Frameworks
  ﹣各式package

.plist
  ﹣類似Android的manifest,對此程式的初始設定。
.xib(.storyboard)
  ﹣類似Android的各戈心一layout.xml
UIApplication
  ﹣一切開始的源頭,類似Android的Activity。
-----

資料出處《Object-C與iOS開發入門》

2011年11月20日 星期日

iOS - Training (3) - 常用類別注意事項

Mutable -----
加上Mutable字眼的物件表示其包含了可更改內容的方法,如:NSString、NSMMutableString,NSMutableString繼承了NSString,差別在NSMutableString加上了append、delete、insert、replace等方法。
而集合類的NSMutableSet也是繼承了NSSet,並加了add、insert、remove等方法。
-----
集合類別 -----
NSSet與NSArray大至上與Java的Set與Array沒什麼分別;NSDictionay如同Java中的Map類別,也是key-value的概念。

集合類別的注意事項:

  1. 一開始加入集合中的物件,最後需加上"nil“來代表集合結束。
  2. Mutable類一開始加入時也需要使用"nil"代表結束,但在使用append等方法時,就不需加上"nil“來表達結束。
  3. 由於集合中"nil"代表結束,當要放入null時,需要使用NSNull。
  4. 不可在迴圈中add或remove物件,不然會造成集合中的元素編號不對,而發生例外。
  5. 集合中不能有基本型態存在,只能在存物件。當需要放入基本型態時,要使用NSNumber來轉換成物件,再放入集合中。
  6. 需要使用集合中的iterator時,在迴圈中使用"in"保留字。
-----

資料出處《Object-C與iOS開發入門》

iOS - Training (2) - Protocol & Delegate

Protocol -----
與iOS自有的interface相似,不同在於protocol中無法宣告變數。

Usage
宣告一個叫Omniprinter的protocol
@protocol Omniprinter
-(void) printInt:(int) intVar;
-(void) printObj:(NSString*) obj;
@end
在interface採用
@interface Hello:NSObject<omniprinter>
@end
若採用多個protocol時
@interface Hello:NSObject<omniprinter, protocol1, ...>
@end

其特性與Java的interface相同,比較特別的是在protocol當中可以加入修飾詞optional、required來決定是否一定要實作,預設值為required。

Usage
@protocol Omniprinter
@optional
-(void) printInt:(int) intVar;
@required
-(void) printObj:(NSString*) obj;

在iOS中,類似泛型的用法如下:
表示變數的實體物件必需滿足Omniprinter這個protocol所宣告的方法
id<Omniprinter> delegate;

表示類別變數的實體物件必需是Hello類別,且Hello類別必需實作Omniprinter這個protocol內的方法
Hello<Omniprinter>* delegate;
-----

資料出處《Object-C與iOS開發入門》

2011年11月16日 星期三

iOS - Training (1) - Memory Management

在iOS 5之前的版本需要處理記憶體相關的動作,在iOS 5加入了ARC功能後,iOS會自行處理沒被參考到的物件,且記憶體相關的API會不能使用

NSObject -----
Hello* hello = [Hello new]; 等同於
Hello* hello = [[Hello alloc] init];

new, alloc為NSObject中的行為,而init為NSObject中的建構子,
所以在定義時建議使用[[Hello alloc] init],會比較清楚。

在產生一個新物件時,物件的retainCount會預設為1
retain行為將物件的retainCount加1
release行為將物件的retainCount減1
copy行為產生一個新物件,但內容與舊物件相同,但為不同的記憶體位置,retainCount為1 (Java裡的clone)
PS NSArray, NSDictionary等Immutable物件copy後的記憶體位置不變,故copy與retain都會將retainCount加1
-----
NSAutoreleasePool -----
當行為需回傳一個物件時
-(Hello*) genHello
{
    Hello* wholeNewHello = [[Hello alloc] init];
    return wholeNewHello;
}
會造成memory leak,需要使用release行為,但若在return之前加入release的話會造成錯誤,因為物件在回傳之前retainCount為0而被回收掉
為解決此問題,使用autorelease行為
-(Hello*) genHello
{
    Hello* wholeNewHello = [[Hello alloc] init];

    return [wholeNewHello autorelease];
}
使用autorelease行為可以先將物件丟入autorelease pool裡,待資料傳遞結束後再將此物件釋出
以下程式碼示範用法
int main(int argc, const char* argv[])
{
    NSAutoreleasePool* pool1 = [[NSAutoreleasePool alloc] init];
    NSAutoreleasePool* pool2 = [[NSAutoreleasePool alloc] init];
    NSMutableString* str = [NSMutableString string];
    [str retain];
    NSLog(@"retain count %d", [str retainCount]);
    [pool1 drain];
    NSLog(@"retain count %d", [str retainCount]);

    return 0;
}
呼叫[pool1 drain]後,OS會將autorelease stack裡的pool1裡頭的物件和其上方的pool2裡的物件作release行為,故結果如下
retain count 2
retain count 1
Pool Stack中的關係如下

由上圖所示,當pool1呼叫drain行為時,pool1及pool2裡的物件會被執行一次release行為,故結果retain count才會由一開始的2變成1

建構子或行為前的修飾字元
"+": 與Java的static相同,執行完後面的行為後隨時會被回收
"-": 與Java的public相同,執行完後面的行為後因為有實體的記憶體位置,故會放入release pool裡等待釋放
-----
Property -----
可少寫getter與setter方法。
其修飾詞包括了readonly、assign、nonatomic來管理記憶體。

Usage
在interface宣告時使用property,
@interface Translator:NSObject
{
    Hello* lang;
}
@property (retain) Hello* lang;
@end
而在implement時使用synthesize。
@implementation Translator
@synthesize lang;
@end
------

資料出處《Object-C與iOS開發入門》

2011年11月13日 星期日

JAVA - Real-time Transport Protocol (RTP)

RTP -----
由Internet radio, video conferencing, video-on-demand, and video telephony的共通特性所發展出來的協定

-----
Java Media Framework (JMF) -----
需先下載JMF
MediaLocator物件與URL物件相似, 差別在URL需要URLStreamHandler處理, 而MediaLocator不用, 故在系統沒有安裝URLStreamHandler時, 使用MediaLocator為佳
-----