這邊要討論的是如何從別人的程式改起 變成自己的東西
站在巨人的肩膀 總是比自己單搞看的遠
(其實是我根本沒辦法從頭開始寫一個程式 XD....)
還有如何用toolchain開發一個程式(當然SDK也很好 但是偏偏我要寫東西的SDK使用協定不讓我寫XD)

先抱怨一下

我實在是搞不定SDK with toolchain
用 Linux編輯的拿到iPhone上頭也不能用 囧mmmmmm
所以只能回到iPhone上頭用他的toolchain 2.0編輯軟體
相當麻煩 有沒有哪位大德 可以教教我怎樣在Mac 上面建立 Toolchain的環境阿?

好先說明你需要的東西
1.一個jailbreak的iPhone 軔體版本在2.0以上
2.透過Cydia 安裝openssh toolchain2.0(相當大喔~~)
3.一個ssh軟體 讓你可以登入iPhone寫程式
4. HelloWorld source code在 這邊 還有改過的 HelloWorld source code

首先先把HelloWorld先給解壓縮到iPhone裡面
假設你是放在 /var/root/底下
那程式碼就在 /var/root/HelloWorld/Classes/底下

裡面有三個檔案
main.m
HelloWorld.m
HelloWorld.h

main.m ->如果你寫的程式相當小 大致上都不需要改他
HelloWorld.h -> 將要用到的東西都登記起來
HelloWorld.m -> 真正的程式碼

讓我們看看其中一個程式碼
代碼:
#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#import <UIKit/UIWindow.h>
#import <UIKit/UIKit.h>
#import <UIKit/UIApplication.h>
#import <UIKit/UITextView.h>
#import <UIKit/UINavigationBar.h>
#import <UIKit/UIView.h>
#import <UIKit/UIAccelerometer.h>

*interface HelloWorldApp : UIApplication <UIAccelerometerDelegate> {
    UIView            *mainView;
    UITextView        *textView;
}
*end
前面第一段的#import 跟傳統的 #include是差不多的
後面的*interface HelloWorldApp: UIApplication
是宣告一個繼承自UIApplication 的Class叫HelloWorldApp
<UIAccelerometerDelegate> 這段 原諒我笨.... 我不知道他是幹嘛的....
{}裡面的就是程式在運作中間都會運用到的變數
*end代表這個宣告結束了
你可以宣告很多個Class 或者跟我一樣懶懶得用一個Class搞定一切(當然這會造成維護困難 囧mmm)

至於HelloWorld.m呢 就是說明 我們這個程式實際要做的事情
代碼:
#import "HelloWorldApp.h"

*implementation HelloWorldApp
- (void) applicationDidFinishLaunching: (id) unused
{
    UIWindow *window;

    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    /* Create a text view */
    textView = [[UITextView alloc]
        initWithFrame: CGRectMake(0.0f, 48.0f, 320.0f, 100.0f)];
    [textView setText:*"Ciao"];

    /* Create a main view, and add our text view as subview */
    mainView = [[UIView alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
    [mainView addSubview:textView];

    /* Setup window */
    [window makeKeyAndVisible];
    [window addSubview: mainView];
}
*end
很簡短 所以很好解釋
首先你可以看到第一行 #import "HelloWorld.h" 你要先把你剛剛設訂的東西給包括進來
代碼:
*implementation HelloWorldApp
...
*end
代表Class HelloWorldApp實作的部份
裡頭有的唯一一個function叫做 - (void) applicationDidFinishLaunching: (id) unused
這個function會在程式被開啟的時候被呼叫(所以你想像他就是整個程式的啟始點就好了)
詳細的Objective-C語法說明 請參閱chenhai在OIKOS的精闢講解
看到這個function的裡面
代碼:
    UIWindow *window; //每一個程式都需要一個基本視窗 底下是他的標準起始方法

    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    /* 新增一個 TextView 所有CocoaTouch的視窗Class都會前綴一個UI 來做區別 *\
        而UITextView 你簡單想成一個文字方塊就好了,後面的initWithFrame: 代表
        你需要傳第一個框架給這個TextView 告訴他他自己有多大 這樣iPhone才畫的
        出來。
        CGRectMake就是幫助你畫出這個框架的工具 第一個0.0f代表從畫面左上角原
        點算起X軸的位置 第二個48.0f代表Y軸位置 第三個320.0f代表框架長度 100.
        0f代表寬度 所以這就是一個 從畫面左上角原點算起 向下48.0f距離 大小320X
    \* 100 的方塊 而他的文字內容是[at]"Ciao" Objective-C的字串 前面要加上[at]*/

    textView = [[UITextView alloc]
        initWithFrame: CGRectMake(0.0f, 48.0f, 320.0f, 100.0f)];
    [textView setText:*"Ciao"];

    /* 新增一個主要的顯示View 大小跟視窗本身一樣大 */

    mainView = [[UIView alloc] initWithFrame: [[UIScreen mainScreen] bounds]];

    /* 將剛剛的TextView給加上去 所有的iPhone畫面你都可以想成用描圖紙畫圖    *\
    \* 所有的畫面都是用貼的一層一層貼上去的 所以這一行把TextView黏在主畫面上*/

    [mainView addSubview:textView];

    /* 設定視窗本身 然後把主畫面黏在視窗上面 */

    [window makeKeyAndVisible];
    [window addSubview: mainView];
好啦我們要開始寫我們自己的東西啦

一個離線地圖工具做的事情大致上是這樣:
因為iPhone上頭的google Maps會從Google下載地圖資料(圖片)
然後把地圖資料用資料庫的格式 存在 /var/mobile/Library/Cache/MapTiles/Maptiles.sqlitedb 這個檔案裡面
將關於這個地圖的中心座標位置 還有是第幾層地圖 這些資料 存在/var/mobile/Library/Preferences/com.apple.Maps.plist 這個檔案裡面

所以離線地圖應該是可以讓你透過PC上面的GMDL下載圖資
然後用它幫助你切換多個地圖資料
(Point在切換 跟 多個圖資 如果你只有一個地圖 那你大可直接用覆蓋的 或是用ilm2這個程式就好了)
只不過FW 2.2之後圖資格式改變了 需要用工具修改 詳情請看這裡(俄文)
這樣你就不必浪費iPhone上面的網路頻寬去下載圖資
可以同時擁有多個地圖資料 並且可以執行切換

好 現在目標出來了
我們需要一個畫面來說明

sorry 註解的部份我弄不出來
請到flickr這邊看

簡單來說
要達到跟原來離線地圖OfflineMaps差不多的功能
我們需要一個Window ,一個mainView,一個NavigationBar(就是上面的那條標題欄),一個TableView畫面,一個儲存要顯示資料的陣列

所以更改過的HelloWorld.h大概是這樣
代碼:
#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#import <UIKit/UIWindow.h>
#import <UIKit/UIKit.h>
#import <UIKit/UIApplication.h>
#import <UIKit/UITableView.h>
#import <UIKit/UINavigationBar.h>
#import <UIKit/UIView.h>
#import <Foundation/NSFileManager.h>
#import <UIKit/UIAlert.h>
#import <Foundation/NSString.h>

*interface HelloWorldApp : UIApplication <
	UITableViewDataSource,
	UITableViewDelegate
> {
	    UIView            *mainView;
	    UIWindow *window;
	    UITableView *table;
	    UINavigationBar *navbar;
	    //主要的資料陣列
	    NSMutableArray *list;
	
}
*end
跟原本的很像 但是有點不一樣
大致上就是多了一些import檔案 因為我們用到的東西比較多
至於HelloWorld.m則需要比較大的變動

剛開始仍然不變 我們的啟始點仍然在- (void) applicationDidFinishLaunching: (id) unused
多出來的那些 大部分是牽涉到TableView的詳細表現

首先我們先看到 - (void) applicationDidFinishLaunching: (id) unused 的內容
代碼:
    //這邊大同小異
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
    /* Create a main view */
    mainView = [[UIView alloc] initWithFrame: [[UIScreen mainScreen] bounds]];

    //開始新增一個NavigationBar 並且把它黏上去
    navbar =  [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 64.0f)];
    [navbar setDelegate: self];
    [navbar pushNavigationItem:[[UINavigationItem alloc] initWithTitle: *"OfflineMaps"] animated : true];
    [mainView addSubview:navbar];

    //開始設計我們的內容 把表單起始化
    list = [[NSMutableArray alloc] init];
	for ( int i = 0; i<4 ; i++) [list addObject:[[NSMutableArray alloc] init]];
        // 這邊分成四個區塊 
	// 0 => Maps
	// 1 => Routes
	// 2 => BookMarks
	// 3 => Info
	
    // 這是用來記錄讀進來的檔案的陣列
    NSMutableArray *maps = [[NSMutableArray alloc] init];
    // Objective-C讀取檔案用的工具 你可以想成 一個檔案總管
    NSFileManager *manager = [NSFileManager defaultManager];
    // Objective-C讀取檔案夾裡面的檔案是透過 NSString 字串的傳遞 這點還滿有趣的
    // 將一個路徑的字串交給 manager 他會把內容物丟到maps這個陣列裡面去
    // stringWithFormat:是讓你用一個模版來輸出字串 %[at]用後面 , 的字串取代
    // 很抱歉 因為論壇系統會擋掉小老鼠[at] 所以詳細的內容還是看source code好了

    [maps addObjectsFromArray:[manager contentsOfDirectoryAtPath:[NSString stringWithFormat:*"%[at]/Media/Maps", NSHomeDirectory()] error:NULL]];
    for (NSString *path in maps) {
        //確認一下這個檔案是不是資料夾 這下面的程式碼是從 Apple Developer的Reference抄來的...
		BOOL isDir;
		if ([manager fileExistsAtPath: [NSString stringWithFormat:*"/var/mobile/Media/Maps/%*",path] isDirectory: &isDir ] && isDir){
			// 如果這個檔案是資料夾 那麼就在辨別 底下是否存在該存在的檔案
			NSString *folder = [NSString stringWithFormat:*"/var/mobile/Media/Maps/%*",path];
			if([manager fileExistsAtPath:[NSString stringWithFormat:*"%*/MapTiles.sqlitedb",folder]]){
			// 好啦 看起來是 那就把這個檔案加入Map那一個區塊
				[[list objectAtIndex: 0 ] addObject: path];
                        // 這邊就是Objective-C最特別的地方 用不完的中括號
			}
		}
		else{
			
			// 如果不是資料夾 那麼他也許是路徑檔 或者是書籤檔
			NSLog(*"Checking Bookmark or Routes"); 
                        // 這是輸出除錯的資料 不過 我自己是很不會看 log 
                        // 底下這段是用來辨別檔案是 Route或是BookMark的
                        // 也許有更漂亮的寫法 但是 我也是新手 我只追求可行
			NSDictionary * plist = [NSMutableDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:*"/var/mobile/Media/Maps/%*",path]];
			NSString *description = [plist description];
                        
                        //辨別 檔案開頭第六個字元開始的九個字元 形成的字串是
			NSString *checker = [description substringWithRange: NSMakeRange(6,9)];
			// detecting bookmark or Route
			if ([checker isEqualToString:*"Bookmarks"])
			{	//如果是書籤 就加入書籤這一塊
				[[list objectAtIndex: 2] addObject: path];
			}
			else
			{      //如果不是書籤 就加入路徑這一塊
				[[list objectAtIndex: 1] addObject: path];
			}
		}
    }
        //加上一些細部功能
	if([[list objectAtIndex:0] count] != 0){
		[[list objectAtIndex:0] addObject: *"Remove Cache"];
	}
	if([[list objectAtIndex:1] count] != 0){
		[[list objectAtIndex:1] addObject: *"Remove Route"];
	}
	if([[list objectAtIndex:2] count] != 0){
		[[list objectAtIndex:2] addObject: *"Remove Bookmark"];
	}
	[[list objectAtIndex:3] addObject: *"Instruction"];
	
	table = [[UITableView alloc] initWithFrame:CGRectMake(0.0f,60.0f,320.0f,420.0f) style: 1];
	// 1 代表 以群組區分的表單 0 代表 平面的表單
	[mainView addSubview: table];
	[table setDataSource:self];
        [table setDelegate:self];

    [table setEditing:NO animated:NO];
    table.allowsSelectionDuringEditing = YES;

    [table setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
    /* Setup window */
    [window makeKeyAndVisible];
    [window addSubview: mainView];
至於其他function 有包括TableView的那些林林總總 則是規範表單的呈現方式
這個部分我就建議大家去看看iPhone Develop Reference 有關 UITableView的部份了
打算怎麼呈現表單的形狀 方式 有很多變化 大家可以發揮創意來操作
當一切都完成了
大家可以在/var/root/HelloWorld目錄下面
執行 make ; make install
然後重開機 看看結果

當然詳細的程式還有許多需要改的地方
這篇只是一個記錄 以免我年老忘記自己寫的步驟
Google大神雖然可以告訴我們很多事情
但是有些給的答案太多 有些又不是我們要的 所以 不寫下來真的以後會忘記
附上的檔案裡頭的code有些有加上註解 有些則忘記了 XD
大家有問題可以一起討論 ... 如果有些更好的寫法也請教我 謝謝