站在巨人的肩膀 總是比自己單搞看的遠
(其實是我根本沒辦法從頭開始寫一個程式 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
後面的*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
裡頭有的唯一一個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];
這個部分我就建議大家去看看iPhone Develop Reference 有關 UITableView的部份了
打算怎麼呈現表單的形狀 方式 有很多變化 大家可以發揮創意來操作
當一切都完成了
大家可以在/var/root/HelloWorld目錄下面
執行 make ; make install
然後重開機 看看結果
當然詳細的程式還有許多需要改的地方
這篇只是一個記錄 以免我年老忘記自己寫的步驟
Google大神雖然可以告訴我們很多事情
但是有些給的答案太多 有些又不是我們要的 所以 不寫下來真的以後會忘記
附上的檔案裡頭的code有些有加上註解 有些則忘記了 XD
大家有問題可以一起討論 ... 如果有些更好的寫法也請教我 謝謝