2010年10月8日 星期五
View Transition Tutorial
在 iPhone App 上 View 和 View 之間的轉換非常常見, 這邊提供一個簡單範例教導如何在 View 之間做不同效果的轉換.
開始此範例之前, 必須先準備兩個不同的 view (這部份就不額外說明), 以筆者的例子而言, 有兩個 View, 一個是 OneView, 另一個則是 AnotherView, 在這兩個 View 裡面都包含了單一個 Button, 當點擊之後就可以在這兩個 View 之間做轉換.
真正要寫 Transition 的 code 其實很簡單, 以筆者的例子而言, 有兩種轉換方式, 其程式碼如下
開始此範例之前, 必須先準備兩個不同的 view (這部份就不額外說明), 以筆者的例子而言, 有兩個 View, 一個是 OneView, 另一個則是 AnotherView, 在這兩個 View 裡面都包含了單一個 Button, 當點擊之後就可以在這兩個 View 之間做轉換.
真正要寫 Transition 的 code 其實很簡單, 以筆者的例子而言, 有兩種轉換方式, 其程式碼如下
-(void)FlipAnotherFromOne{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:window cache:YES];
[oneController.view removeFromSuperview];
[window addSubview:anotherController.view];
[UIView commitAnimations];
}
-(void)FlipOneFromAnother{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:window cache:YES];
[anotherController.view removeFromSuperview];
[window addSubview:oneController.view];
[UIView commitAnimations];
}
上面程式碼中的 setAnimationTransition 在 iPhone 裡面定義如下:
typedef enum {
UIViewAnimationTransitionNone,
UIViewAnimationTransitionFlipFromLeft,
UIViewAnimationTransitionFlipFromRight,
UIViewAnimationTransitionCurlUp,
UIViewAnimationTransitionCurlDown,
} UIViewAnimationTransition;
所以若需要不同的效果只要填上自己所需要的參數即可.
需要完整的範例可到 這裡 下載
2010年10月7日 星期四
Interface Builder Tutorial - 1
一般開發者在開發界面程式時, 都會需要借助視覺化的工具來縮短開發時間, 例如最常看到的如 Borland C++ Builder (BCB). 這類工具主要概念是"所見即所得" , 簡單的說就是一些常看到的使用者介面如 button, label, textfield, ....等, 都可以透過 BCB 等這類工具讓開發者直接用拖曳元件的方式來快速達成開發目標, 當然這些工具都是屬於 windows 的, 那究竟在 iPhone Programming 的領域中是否也有這類工具呢? 不怎麼貼心的 Apple 當然也提供了視覺化開發工具 "Interface Builder".
在筆者使用的這段時間內, 發現到雖然 Interface Builder 可以快速縮短開發時間, 這點的確是不容置疑的, 但在使用上卻發現許多不直覺的地方 (相對於 windows 上的視覺化開發工具而言), 不過整體來說是還可以接受的範圍.
要開始使用 Interface Builder 之前, 你可以先用 Xcode 新建一新的 iPhone Project, 建立之後會在左側 Project tree 裡看到一個屬於 Interface Builder 的檔案"MainWindow.xib"
直接點擊此檔案開啓 Interface Builder, 在裡面會看到幾個基本的元件 .xib window,
Inspector
Libaray,
我們要做的第一步驟就是直接編輯 window 視窗, 首先直接點擊 .xib 裡的 window, 且在右方 Library 裡找到 "Label" 和 "Round Rect Button", 並直接將她們拖曳到 window 畫面中, 分別編輯上面的顯示字串, 完成時大概會有類似下面的圖示
完成之後, 接下來就是要開始編寫 button & label 相關的 code 了, 在 AppDelegate.h 裡加入 class member & function
並且在 AppDelegate.m 裡面實作 BtnAction, 我們在裡面實作了一個非常簡單的功能, 就是當點擊時, 會去更改 Label 的顯示字串
在筆者使用的這段時間內, 發現到雖然 Interface Builder 可以快速縮短開發時間, 這點的確是不容置疑的, 但在使用上卻發現許多不直覺的地方 (相對於 windows 上的視覺化開發工具而言), 不過整體來說是還可以接受的範圍.
要開始使用 Interface Builder 之前, 你可以先用 Xcode 新建一新的 iPhone Project, 建立之後會在左側 Project tree 裡看到一個屬於 Interface Builder 的檔案"MainWindow.xib"
直接點擊此檔案開啓 Interface Builder, 在裡面會看到幾個基本的元件 .xib window,
Inspector
Libaray,
我們要做的第一步驟就是直接編輯 window 視窗, 首先直接點擊 .xib 裡的 window, 且在右方 Library 裡找到 "Label" 和 "Round Rect Button", 並直接將她們拖曳到 window 畫面中, 分別編輯上面的顯示字串, 完成時大概會有類似下面的圖示
完成之後, 接下來就是要開始編寫 button & label 相關的 code 了, 在 AppDelegate.h 裡加入 class member & function
IBOutlet UILabel *text;
-(void) BtnAction:(id) sender;
並且在 AppDelegate.m 裡面實作 BtnAction, 我們在裡面實作了一個非常簡單的功能, 就是當點擊時, 會去更改 Label 的顯示字串
-(void) BtnAction:(id) sender{
[text setText:@"sYou Pressed!"];
}
當完成上述動作之後, 最後我們要做的就是將 UI 和 code 之間做個"關連", 這步驟就是和一般 window 最大不同的地方, 一般若漏掉"關連"這動作, 你所寫的 code 就不會反應到畫面上, 而"關連"的方式很簡單, 我們再次打開 MainWindow.xib, 然後在 MainWindow.xib 視窗裡選擇 xxxx_AppDelegate,此時就會在 Connection Inspector 裡看到剛剛所新增的 text & BtnAction
"關連" 這動作只要你按住 text 右方的圈圈, 並拖拉至剛剛所產生的 Label 即可, 而 BtnAction 也是一樣, 拖拉到 Button 的位置上, 但是因為觸發 Button 的方式有很多種, 所以當你拖曳過去之後, Interface Builder 會要你選擇觸發方式, 我們此時選 "Touch Up Inside"
2010年10月4日 星期一
HealthyDiet Update version 1.0.1
HealthyDiet 上架之後, 大家給了些不錯的意見, 有把這些建議加入程式, 並且也修正了一個跳出的 bug, 詳細如下:
1. 新增信件備份帳號名稱密碼
2. 修正點選活動量大時會跳出問題
3. 新增份量設定
4. 新增 HealthyDietLite 免費試用版本
1. 新增信件備份帳號名稱密碼
2. 修正點選活動量大時會跳出問題
3. 新增份量設定
4. 新增 HealthyDietLite 免費試用版本
ipa 上傳失敗問題
今天為了更新一些東西需要上傳 binary 到 apple, 但是不知道為什麼又出現
Application failed codesign verification
看到的時後差點沒吐血!!!! 明明昨天就好好的, 上個班回來又變這樣!!!
研究了好久發現一個可以正常 sign app 的方式了, 不過這方是是基於憑證都設定正確的情況下才有用(一定會有人問, 為什麼憑證都設定正確卻還不過, 老實說, 我也不知道, 我只知到心中一百個 OOXX)
1. 第一次直接編譯產生 .ipa
2. 將 Entitlements.plist 砍掉
3. 再次新增 Entitlements.plist
4. 重新編譯, 重點來了!! 要編譯第二次時, 不能 clean project, 只需要把第一次產生的.ipa 砍掉就好(別問我為什麼, 我也不知道)
依照上面步驟編譯完成時, 應該可以在 build log 看到如下訊息
此時的 .ipa 就可以正常上傳了
以上就筆者個人開發環境而言是可以正常運作的, 若你真的對這錯誤走投無路的話, 不訪試試看這方是
Application failed codesign verification
看到的時後差點沒吐血!!!! 明明昨天就好好的, 上個班回來又變這樣!!!
研究了好久發現一個可以正常 sign app 的方式了, 不過這方是是基於憑證都設定正確的情況下才有用(一定會有人問, 為什麼憑證都設定正確卻還不過, 老實說, 我也不知道, 我只知到心中一百個 OOXX)
1. 第一次直接編譯產生 .ipa
2. 將 Entitlements.plist 砍掉
3. 再次新增 Entitlements.plist
4. 重新編譯, 重點來了!! 要編譯第二次時, 不能 clean project, 只需要把第一次產生的.ipa 砍掉就好(別問我為什麼, 我也不知道)
依照上面步驟編譯完成時, 應該可以在 build log 看到如下訊息
/Users/tixlo/Data/iPhone/GameDev/HealthyDiet/build/Distribution-iphoneos/HealthyDiet.app: replacing existing signature
此時的 .ipa 就可以正常上傳了
以上就筆者個人開發環境而言是可以正常運作的, 若你真的對這錯誤走投無路的話, 不訪試試看這方是
2010年10月2日 星期六
Apploader 上傳 ipa 問題
原本上載 ipa 檔已經都搞定, 但是昨天晚上要再次上傳時又發生
application failed codesign verification ....... submission certification
完全不知道為什麼, 照著之前的解決步驟也無法順利上傳, 又試了整個晚上到今天忽然又可以完整的上傳, 過程中嘗試很多方式, 也不知道到底是哪個步驟完整解決此問題, 所以乾脆就直接把有嘗試過的方法都列出來, 以後再遇到的時候再一個一個測試 Orz
1. 將 Entitlements.plist 刪除並重新新增至 Project 根目錄
2. 修改 Project .pbxproj 檔案, 內容如下:
3. 將之前安裝的 certification & key 全部刪除, 把整個 key 的產生, certification 的上傳, Profile 設定步驟全部砍掉重練(keychain & iPhone Provision 都砍掉), 照著步驟重新安裝 certification / provision
依筆者的情況, 依照上面這些步驟"亂試" 結果試可以正常運作的, 不確定其他人是否也可以依此方式解決問題 Orz.., 弄到目前只有一個想法
"Apple 阿 Apple 阿, 我真是不懂你阿!!!"
application failed codesign verification ....... submission certification
完全不知道為什麼, 照著之前的解決步驟也無法順利上傳, 又試了整個晚上到今天忽然又可以完整的上傳, 過程中嘗試很多方式, 也不知道到底是哪個步驟完整解決此問題, 所以乾脆就直接把有嘗試過的方法都列出來, 以後再遇到的時候再一個一個測試 Orz
1. 將 Entitlements.plist 刪除並重新新增至 Project 根目錄
2. 修改 Project .pbxproj 檔案, 內容如下:
依筆者的情況, 依照上面這些步驟"亂試" 結果試可以正常運作的, 不確定其他人是否也可以依此方式解決問題 Orz.., 弄到目前只有一個想法
"Apple 阿 Apple 阿, 我真是不懂你阿!!!"
2010年10月1日 星期五
[iPhone] HealthyDiet
Description
在現今食物熱量普遍都偏高的情況下,導致體重的控制變成是一件非常不容易的事情,要如何有效且健康的維持體重,幾乎變成了全民都需要留意的事情,HealthyDiet 提供了一個科學化的紀錄方式,讓每個使用者都可以精準的控制每天吃進肚子的熱量,根據統計少/多攝取 7700 大卡就平均會瘦/胖 1 kg,有鑒於此,對於每日熱量的控管就變的相當重要,HealthyDiet 可以協助你達上熱量控管的目的,而她所提供的功能有
自動計算個人每天所需要的熱量,理想體重以及BMI可以自行設定所要達到的目標體重,不管是增重還是減肥,HealthyDiet 會根據你所設定的目標(包含目標體重,預計多久達成,本身活動系數),來計算每日應當攝取的熱量內建多達2千多種食物熱量表分類多達 23 種,其中包含大家最長看到的麥當勞,吉野家,肯德基,爭鮮壽司,麵包類,五穀類,飲料,和各個便利商店, 因為中國人食物樣式太多樣話,所以也提供讓你自建食譜, 每個月會有熱量曲線圖,能夠讓你更精準的瞭解每個月的熱量攝取情形
在現今食物熱量普遍都偏高的情況下,導致體重的控制變成是一件非常不容易的事情,要如何有效且健康的維持體重,幾乎變成了全民都需要留意的事情,HealthyDiet 提供了一個科學化的紀錄方式,讓每個使用者都可以精準的控制每天吃進肚子的熱量,根據統計少/多攝取 7700 大卡就平均會瘦/胖 1 kg,有鑒於此,對於每日熱量的控管就變的相當重要,HealthyDiet 可以協助你達上熱量控管的目的,而她所提供的功能有
自動計算個人每天所需要的熱量,理想體重以及BMI可以自行設定所要達到的目標體重,不管是增重還是減肥,HealthyDiet 會根據你所設定的目標(包含目標體重,預計多久達成,本身活動系數),來計算每日應當攝取的熱量內建多達2千多種食物熱量表分類多達 23 種,其中包含大家最長看到的麥當勞,吉野家,肯德基,爭鮮壽司,麵包類,五穀類,飲料,和各個便利商店, 因為中國人食物樣式太多樣話,所以也提供讓你自建食譜, 每個月會有熱量曲線圖,能夠讓你更精準的瞭解每個月的熱量攝取情形
Lite 免費版本已經上架, HealthyDietLite
2010年9月21日 星期二
How to convert .app to .ipa
使用 Xcode 所編譯出來的都是 .app 檔案, 但是大部份情況來說, 使用 .ipa 比 .app 方便多了
因為 .ipa 可以簡單的透過 iTunes 安裝並同步到 iPhone. 既然 ipa 比較方面, 那究竟要如何
達成此幕目的呢? 做法很簡單, 只需要幾個步驟就可以完美產生 .ipa
開啓你的 Xcode 專案, 依照下圖新增一 Build Phase
將下述內容直接複製到 script 欄位中即可
payload_dir="$CONFIGURATION_BUILD_DIR/Payload"
app_bundle_dir="$CONFIGURATION_BUILD_DIR/${PROJECT_NAME}.app"
/bin/rm -rf "$payload_dir"
/bin/mkdir "$payload_dir"
/bin/cp -R "$app_bundle_dir" "$payload_dir"
/bin/cp iTunesArtwork "$CONFIGURATION_BUILD_DIR/iTunesArtwork"
cd "$CONFIGURATION_BUILD_DIR"
/usr/bin/zip -y -r "${PROJECT_NAME}.ipa" Payload iTunesArtwork
rm -rf "$payload_dir" iTunesArtwork
新增 Build Phase 之後, 可以在專案列中看到如下圖般的 "Run Script" 選項
當完成上述步驟時, 只要編譯你的專案 .ipa 就會自動產生 (當然前提是要編譯要成功)
在 build messages 裡面可以看到類似下面的訊息
一切都完成時, 此時就可以看到我們所產生的 .ipa 檔了
因為 .ipa 可以簡單的透過 iTunes 安裝並同步到 iPhone. 既然 ipa 比較方面, 那究竟要如何
達成此幕目的呢? 做法很簡單, 只需要幾個步驟就可以完美產生 .ipa
開啓你的 Xcode 專案, 依照下圖新增一 Build Phase
payload_dir="$CONFIGURATION_BUILD_DIR/Payload"
app_bundle_dir="$CONFIGURATION_BUILD_DIR/${PROJECT_NAME}.app"
/bin/rm -rf "$payload_dir"
/bin/mkdir "$payload_dir"
/bin/cp -R "$app_bundle_dir" "$payload_dir"
/bin/cp iTunesArtwork "$CONFIGURATION_BUILD_DIR/iTunesArtwork"
cd "$CONFIGURATION_BUILD_DIR"
/usr/bin/zip -y -r "${PROJECT_NAME}.ipa" Payload iTunesArtwork
rm -rf "$payload_dir" iTunesArtwork
新增 Build Phase 之後, 可以在專案列中看到如下圖般的 "Run Script" 選項
在 build messages 裡面可以看到類似下面的訊息
一切都完成時, 此時就可以看到我們所產生的 .ipa 檔了
Questions for uploading .ipa binary to apple
case 1:
"The CodeResources file must be a symbolic link to _CodeSignature/CodeResources"
If you're using command-line "zip", you need to use
reference
case 2:
Application failed codesign verification. The signature was invalid, or it was not signed with an Apple submission certificate.
用文字編輯器開啓 .pbxproj , 搜尋 CODE_SIGN_IDENTITY 應該會看到類似下面的話面
以筆者的例子而言, 要找的是 Distrubution , 除了 Distribution 以外, 要留意的有幾個要確定的地方
確認在 buildSettings 裡的 CODE_SIGN_IDENTITY , PROVISIONING_PROFILE (有兩組)
是否為所需要的. 問題就來了, 要怎樣才能知道我們所要的資訊是什麼呢?
針對 CODE_SIGN_IDENTITY , 我們需要開起 Mac 裡的 Keychain Access , 並選擇左下方的“憑證”欄位
打開所需要的憑證
觀看其內容會發現有一個欄位寫著憑證名稱, 此名稱就是要填入到上述 CODE_SIGN_IDENTITY 的值
置於另一個 PROVISIONING_PROFILE 呢? 此時請打開 Xcode | Windows | Organizer
上述的 Profile Identifier 所顯示的值就是我們需要填入 PROVISIONING_PROFILE 的.
上述兩組值需要根據你所申請的 Certification/Provision 的實際情況來填寫
reference
"The CodeResources file must be a symbolic link to _CodeSignature/CodeResources"
If you're using command-line "zip", you need to use
zip -y -r
to preserve the symlink.reference
case 2:
Application failed codesign verification. The signature was invalid, or it was not signed with an Apple submission certificate.
用文字編輯器開啓 .pbxproj , 搜尋 CODE_SIGN_IDENTITY 應該會看到類似下面的話面
確認在 buildSettings 裡的 CODE_SIGN_IDENTITY , PROVISIONING_PROFILE (有兩組)
是否為所需要的. 問題就來了, 要怎樣才能知道我們所要的資訊是什麼呢?
針對 CODE_SIGN_IDENTITY , 我們需要開起 Mac 裡的 Keychain Access , 並選擇左下方的“憑證”欄位
置於另一個 PROVISIONING_PROFILE 呢? 此時請打開 Xcode | Windows | Organizer
上述兩組值需要根據你所申請的 Certification/Provision 的實際情況來填寫
reference
2010年9月17日 星期五
How to implement customized keyboard on iOS4
在 iPhone 上面開發應用程式時, 在使用輸入鍵盤時, 或多或少都會遇到客制化鍵盤的問題, 這邊筆者以
簡單的數字鍵盤來示範客制化的動作. 這部份我想網路上已經有不少 sample code , 但大部份基本上都是
以 SDK 3.x 的版本去實作, 以"特定寫法"來實作客制化在 iOS4 會有問題, 這部份稍候會提到兩版本的差異.
上述看到的例子是 UIKeyboardTypeNumberPad 搭配 "Done" 的圖示所組合而成的. 在開
始介紹如何實作之前, 先稍微提一下網路上查到的一些範例寫法. 因為 SDK 升版之
後在架構上有做了些修改, 所以導致行為上的不正確. 以下面這例子為例, 它可以正
常的在 iOS4 之前的版本運行, 但在 iOS4 上卻會有看不到上面 "Done" 圖示的問題.
上述這段代碼主要原理是透過跟 OS 註冊 keyboard 相關的 notification, 並在顯示
keyboard 時, 在 keyboard view 上添加所需要的特定 UIView, 簡單流程大致如下
1. 註冊 UIKeyboardWillShowNotification : 當 keyboard 要秀時, OS 就會呼叫
keyboardWillShow
2. 當被 keyboardWillShow 叫用時, 搜尋屬於 keyboard 的 view
if([[keyboard description] hasPrefix:@"<UIKeyboard"] == YES)
3. 當找到所需要的 view 時, 再將需要的 view 加入即可
[keyboard addSubview:doneButton];
上面就是一個 customized keyboard 的簡單實作流程. 但是為什麼這段 code 會無法
在 iOS4 上正確執行呢? 問題點主要出在上述的第 2 個步驟.
在舊的 SDK 中, 當 UIKeyboardWillShowNotification 事件發生且叫用 keyboardWillShow
時, 此時的 keyboard view 已經被添加到 windows 裡了, 但是在 iOS4 的世界中, 相同
情況發生時, keyboard view 卻會在下個 event loop 裡才會被添加到 windows 中, 也
就是因為如此, 所以上述
[[[UIApplication sharedApplication] windows] objectAtIndex:1];
會找不到 keyboard view. 除了這原因以外, 還有另一個重要的差異性, 第 2 步驟所比
對的 @"<UIKeyboard" 字串在 iOS4 中也被修正過, 它被藏在 @"<UIPeripheralHostView"
裡.
針對這兩點, 所以將只要將之修正即可正常的在 iOS4 上執行
1. keyboard view
既然知道是 keyboard view 會在下個 event loop 才會被放到 windows 裡, 所以我們
可以透過下面方式將 keyboardWillShow 延遲叫用
[self performSelector:@selector(keyboardWillShow:) withObject:nil afterDelay:0];
2. 修正比對 @"<UIKeyboard" 的方式
簡單的數字鍵盤來示範客制化的動作. 這部份我想網路上已經有不少 sample code , 但大部份基本上都是
以 SDK 3.x 的版本去實作, 以"特定寫法"來實作客制化在 iOS4 會有問題, 這部份稍候會提到兩版本的差異.
上述看到的例子是 UIKeyboardTypeNumberPad 搭配 "Done" 的圖示所組合而成的. 在開
始介紹如何實作之前, 先稍微提一下網路上查到的一些範例寫法. 因為 SDK 升版之
後在架構上有做了些修改, 所以導致行為上的不正確. 以下面這例子為例, 它可以正
常的在 iOS4 之前的版本運行, 但在 iOS4 上卻會有看不到上面 "Done" 圖示的問題.
- (void)loadView{
...
textFieldContent.delegate = self;
textFieldContent.placeholder = @"press me";
textFieldContent.keyboardType = UIKeyboardTypeNumberPad;
textFieldContent.returnKeyType = UIReturnKeyDone;
[self.view addSubview:textFieldContent];
[textFieldContent release];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
}
- (void)keyboardWillShowOnDelay:(NSNotification *)notification{
UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
doneButton.frame = CGRectMake(0, 163, 106, 53);
doneButton.adjustsImageWhenHighlighted = NO;
[doneButton setImage:[UIImage imageNamed:@"DoneUp.png"] forState:UIControlStateNormal];
[doneButton setImage:[UIImage imageNamed:@"DoneDown.png"] forState:UIControlStateHighlighted];
[doneButton addTarget:self action:@selector(doneButton:) forControlEvents:UIControlEventTouchUpInside];
UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
UIView* keyboard;
for(int i=0; i<[tempWindow.subviews count]; i++) {
keyboard = [tempWindow.subviews objectAtIndex:i];
if([[keyboard description] hasPrefix:@"<UIKeyboard"] == YES)
[keyboard addSubview:doneButton];
}
}
上述這段代碼主要原理是透過跟 OS 註冊 keyboard 相關的 notification, 並在顯示
keyboard 時, 在 keyboard view 上添加所需要的特定 UIView, 簡單流程大致如下
1. 註冊 UIKeyboardWillShowNotification : 當 keyboard 要秀時, OS 就會呼叫
keyboardWillShow
2. 當被 keyboardWillShow 叫用時, 搜尋屬於 keyboard 的 view
if([[keyboard description] hasPrefix:@"<UIKeyboard"] == YES)
3. 當找到所需要的 view 時, 再將需要的 view 加入即可
[keyboard addSubview:doneButton];
上面就是一個 customized keyboard 的簡單實作流程. 但是為什麼這段 code 會無法
在 iOS4 上正確執行呢? 問題點主要出在上述的第 2 個步驟.
在舊的 SDK 中, 當 UIKeyboardWillShowNotification 事件發生且叫用 keyboardWillShow
時, 此時的 keyboard view 已經被添加到 windows 裡了, 但是在 iOS4 的世界中, 相同
情況發生時, keyboard view 卻會在下個 event loop 裡才會被添加到 windows 中, 也
就是因為如此, 所以上述
[[[UIApplication sharedApplication] windows] objectAtIndex:1];
會找不到 keyboard view. 除了這原因以外, 還有另一個重要的差異性, 第 2 步驟所比
對的 @"<UIKeyboard" 字串在 iOS4 中也被修正過, 它被藏在 @"<UIPeripheralHostView"
裡.
針對這兩點, 所以將只要將之修正即可正常的在 iOS4 上執行
1. keyboard view
既然知道是 keyboard view 會在下個 event loop 才會被放到 windows 裡, 所以我們
可以透過下面方式將 keyboardWillShow 延遲叫用
[self performSelector:@selector(keyboardWillShow:) withObject:nil afterDelay:0];
2. 修正比對 @"<UIKeyboard" 的方式
if ([[possibleKeyboard description] hasPrefix:@"<UIPeripheralHostView"])
possibleKeyboard = [[possibleKeyboard subviews] objectAtIndex:0];
if ([[possibleKeyboard description] hasPrefix:@"<UIKeyboard"])
{
foundKeyboard = possibleKeyboard;
break;
}
經過上述兩個修正之後的 code 大概會如下 :
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShowOnDelay:)
name:UIKeyboardWillShowNotification
object:nil];
- (void)keyboardWillShowOnDelay:(NSNotification *)notification
{
[self performSelector:@selector(keyboardWillShow:) withObject:nil afterDelay:0];
}
- (void)keyboardWillShow:(NSNotification *)notification
{
UIView *foundKeyboard = nil;
UIWindow *keyboardWindow = nil;
for (UIWindow *testWindow in [[UIApplication sharedApplication] windows])
{
if (![[testWindow class] isEqual:[UIWindow class]])
{
keyboardWindow = testWindow;
break;
}
}
if (!keyboardWindow) return;
for (UIView *possibleKeyboard in [keyboardWindow subviews])
{
//iOS3
if ([[possibleKeyboard description] hasPrefix:@"<UIKeyboard"])
{
foundKeyboard = possibleKeyboard;
break;
}
else
{
// iOS 4 sticks the UIKeyboard inside a UIPeripheralHostView.
if ([[possibleKeyboard description] hasPrefix:@"<UIPeripheralHostView"])
{
possibleKeyboard = [[possibleKeyboard subviews] objectAtIndex:0];
}
if ([[possibleKeyboard description] hasPrefix:@"<UIKeyboard"])
{
foundKeyboard = possibleKeyboard;
break;
}
}
}
if (foundKeyboard)
{
// create custom button
UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
doneButton.frame = CGRectMake(0, 163, 106, 53);
doneButton.adjustsImageWhenHighlighted = NO;
[doneButton setImage:[UIImage imageNamed:@"DoneUp.png"] forState:UIControlStateNormal];
[doneButton setImage:[UIImage imageNamed:@"DoneDown.png"] forState:UIControlStateHighlighted];
[doneButton addTarget:self action:@selector(doneButton:) forControlEvents:UIControlEventTouchUpInside];
[foundKeyboard addSubview:doneButton];
}
}
這邊要注意到的是, 因為同一個 app 有可能會在新舊版本的 OS 上執行, 所以若只有
針對 iOS4 而做修改, 就有可能會造成舊 iPhone 無法正常值行, 有鑒於此, 上述這段
code 針對比對字串做了點小技巧, 讓此 code 可以符合新舊版本.
完整的範例可以到 這裡 下載
2010年9月12日 星期日
Introduce COM (Component Object Model)
這陣子對於 DirectShow 的研究, 研究的越仔細, 就越覺得 DirectShow 的強大, DirectShow
簡化了多媒體的撥放, 格式的轉換, 也允許開發者可以自訂新的元件. 因 DirectShow 是基於
COM (Component Object Model) 所發展出來的, 其裡面的 Component 都是可以很簡單的被
使用, 但也因為是 COM, 所以要開發 DirectShow 相對的就必須要對 COM 有所了解.
COM 並不是某一種特別的應用, 而是一個你可以用來創造任何形式應用的 Model. 例如 ActiveX
, OLE 都是使用 COM 為基礎來開發, 而 windows 上常看到的 DLL 也是一個 COM 的應用,但
COM 的應用並不只有這樣而已.
"COM 定義了一個建立元件的標準". 原則上每個程式設計師都可以自訂屬於自己的元件標準,
但以這樣的前提下, 每個人所開發出來的元件就可能會有不相容的情形, 而軟體元件只有在我們
都同意使用相同定義時才會有其價值. 因為 COM 的標準, 所以只要所有程式設計師都採用此
規定的標準來實作軟體元件, 這樣這些軟體元件才可以一起運作.
COM 支援 OOP 的三個原則: Encapsulation, Polymorphism, Inheritance.
1. Encapsulation :
透過封裝, 開發者並不需要知道元件內的實作方式或是其演算法, 僅需知道如何與元件聯繫即
可, 對開發者來說, 元件模組是如何實作, 或是用哪種語言實作都不重要, 只有元件模組的
使用方式才重要.
2. Polymorphism :
"一個介面 , 多個方法". 它可以讓開發者自由的將模組給實行成他所適合的模組特性, 因此
你可以明確指示某個元件要如何工作, 而其他則由其他人自行決定要如何運作.
3. Inheritance :
允許一個元件去繼承其他元件功能的特性.
COM 所定義的 interface 中, 最重要的就是 IUnknown, 它是所有 COM 物件都必須要實作的
interface. IUnknown 定義了三個 interface : QueryInterface(), AddRef(), Release().
QueryInterface :
此介面會查詢元件是否實作了某個指定的介面, 若有實作, 則回傳此介面的指標. 如果指定的
interface 沒有被支持的話, QueryInterface() 會回傳 E_NOINTERFACE error code.
AddRef,Release :
在 COM 裡管理方式是透過 reference count 的方式, 當 count 為 0 時, 此物件就會被釋放
以下為 DirectShow 裡 CClassFactory 針對上述三個 interface 的實作
STDMETHODIMP CClassFactory::QueryInterface(REFIID riid,void **ppv)
{
CheckPointer(ppv,E_POINTER)
ValidateReadWritePtr(ppv,sizeof(PVOID));
*ppv = NULL;
// any interface on this object is the object pointer.
if ((riid == IID_IUnknown) || (riid == IID_IClassFactory)) {
*ppv = (LPVOID) this;
// AddRef returned interface pointer
((LPUNKNOWN) *ppv)->AddRef();
return NOERROR;
}
return ResultFromScode(E_NOINTERFACE);
}
STDMETHODIMP_(ULONG) CClassFactory::AddRef()
{
return ++m_cRef;
}
STDMETHODIMP_(ULONG) CClassFactory::Release()
{
if (--m_cRef == 0) {
delete this;
return 0;
} else {
return m_cRef;
}
}
每一個 COM 都會有一個 GUID (Global Unique IDentifier), 譬如 :
GUID 可以透過 GuidGen.exe 來產生, 通常此程式會放置在 [VC/VS2005]/Common/Tools/Bin
當要使用 QueryInterface 來查詢需要的介面時, 第一個傳入參數就是此介面的 GUID
在任何 COM API 被呼叫之前, COM 的初始化是必須的, 可以透過 CoInitialize() 來達到目的.
另外當不在需要 COM 時, 需要呼叫下面函式釋放 COM.
除了 CoInitialize/CoUninitialize 以外, COM 也提供了 CoCreateInstance 來載入 COM
對於 CoCreateInstance , 可能還需要在稍微簡單解釋一下, 他到底是如何運作的. 我們先大致
看一下 CoCreateInstance 的實作流程
HRESULT CoCreateInstance(...)
{
HINSTANCE hinstDll;
DLLGETCLASSOBJECT DllGetClassObject;
hinstDll = LoadLibrary("xxxx.dll");
if (successful)
{
DllGetClassObject = (DLLGETCLASSOBJECT) GetProcAddress(hinstDll,"DllGetClassObject");
if (DllGetClassObject)
hr = DllGetClassObject(...);
}
return hr;
}
上述會看到當呼叫 CoCreateInstance 時, 會有幾個動作須要完成
1. 載入所需要的 dll
2. 拿到 DllGetClassObject function pointer, 此 DllGetClassObject 是由 COM 所定義的 interface
HRESULT __stdcall DllGetClassObject(REFCLSID rclsid,
REFIID riid,
LPVOID *ppv);
呼叫完 CoCreateInstance 時, 理論上應該就可以獲得所需要介面的 pointer 進而操作此
COM 物件. 另外當我們使用 Microsoft 所提供的開發工具建立 COM 專案時, 上述的
DllGetClassObject 我們將不用自己實作, 在 COM 已經完成這些事情了, 我們只需要依照步
驟建立所需要的介面即可.
簡化了多媒體的撥放, 格式的轉換, 也允許開發者可以自訂新的元件. 因 DirectShow 是基於
COM (Component Object Model) 所發展出來的, 其裡面的 Component 都是可以很簡單的被
使用, 但也因為是 COM, 所以要開發 DirectShow 相對的就必須要對 COM 有所了解.
COM 並不是某一種特別的應用, 而是一個你可以用來創造任何形式應用的 Model. 例如 ActiveX
, OLE 都是使用 COM 為基礎來開發, 而 windows 上常看到的 DLL 也是一個 COM 的應用,但
COM 的應用並不只有這樣而已.
"COM 定義了一個建立元件的標準". 原則上每個程式設計師都可以自訂屬於自己的元件標準,
但以這樣的前提下, 每個人所開發出來的元件就可能會有不相容的情形, 而軟體元件只有在我們
都同意使用相同定義時才會有其價值. 因為 COM 的標準, 所以只要所有程式設計師都採用此
規定的標準來實作軟體元件, 這樣這些軟體元件才可以一起運作.
COM 支援 OOP 的三個原則: Encapsulation, Polymorphism, Inheritance.
1. Encapsulation :
透過封裝, 開發者並不需要知道元件內的實作方式或是其演算法, 僅需知道如何與元件聯繫即
可, 對開發者來說, 元件模組是如何實作, 或是用哪種語言實作都不重要, 只有元件模組的
使用方式才重要.
2. Polymorphism :
"一個介面 , 多個方法". 它可以讓開發者自由的將模組給實行成他所適合的模組特性, 因此
你可以明確指示某個元件要如何工作, 而其他則由其他人自行決定要如何運作.
3. Inheritance :
允許一個元件去繼承其他元件功能的特性.
COM 所定義的 interface 中, 最重要的就是 IUnknown, 它是所有 COM 物件都必須要實作的
interface. IUnknown 定義了三個 interface : QueryInterface(), AddRef(), Release().
QueryInterface :
此介面會查詢元件是否實作了某個指定的介面, 若有實作, 則回傳此介面的指標. 如果指定的
interface 沒有被支持的話, QueryInterface() 會回傳 E_NOINTERFACE error code.
AddRef,Release :
在 COM 裡管理方式是透過 reference count 的方式, 當 count 為 0 時, 此物件就會被釋放
以下為 DirectShow 裡 CClassFactory 針對上述三個 interface 的實作
STDMETHODIMP CClassFactory::QueryInterface(REFIID riid,void **ppv)
{
CheckPointer(ppv,E_POINTER)
ValidateReadWritePtr(ppv,sizeof(PVOID));
*ppv = NULL;
// any interface on this object is the object pointer.
if ((riid == IID_IUnknown) || (riid == IID_IClassFactory)) {
*ppv = (LPVOID) this;
// AddRef returned interface pointer
((LPUNKNOWN) *ppv)->AddRef();
return NOERROR;
}
return ResultFromScode(E_NOINTERFACE);
}
STDMETHODIMP_(ULONG) CClassFactory::AddRef()
{
return ++m_cRef;
}
STDMETHODIMP_(ULONG) CClassFactory::Release()
{
if (--m_cRef == 0) {
delete this;
return 0;
} else {
return m_cRef;
}
}
每一個 COM 都會有一個 GUID (Global Unique IDentifier), 譬如 :
B38847F0-4CBE-4705-B55A-D0F13552B385
當要使用 QueryInterface 來查詢需要的介面時, 第一個傳入參數就是此介面的 GUID
在任何 COM API 被呼叫之前, COM 的初始化是必須的, 可以透過 CoInitialize() 來達到目的.
//NotUsed 參數是被保留的, 且必須要為 NULL.
HRESULT CoInitialize(LPVOID *NotUsed);
void CoUninitialize();
除了 CoInitialize/CoUninitialize 以外, COM 也提供了 CoCreateInstance 來載入 COM
HRESULT __stdcall CoCreateInstance(
REFCLSID rclsid, //所要求元件的 CLSID
LPUNKNOWN pUnKOuter, //控制元件的 IUnknown
DWORD dwClsContext, //元件伺服器的執行背景
REFIID riid, //元件所要求的介面
LPVOID *ppv); //傳回元件介面的指標
對於 CoCreateInstance , 可能還需要在稍微簡單解釋一下, 他到底是如何運作的. 我們先大致
看一下 CoCreateInstance 的實作流程
HRESULT CoCreateInstance(...)
{
HINSTANCE hinstDll;
DLLGETCLASSOBJECT DllGetClassObject;
hinstDll = LoadLibrary("xxxx.dll");
if (successful)
{
DllGetClassObject = (DLLGETCLASSOBJECT) GetProcAddress(hinstDll,"DllGetClassObject");
if (DllGetClassObject)
hr = DllGetClassObject(...);
}
return hr;
}
上述會看到當呼叫 CoCreateInstance 時, 會有幾個動作須要完成
1. 載入所需要的 dll
2. 拿到 DllGetClassObject function pointer, 此 DllGetClassObject 是由 COM 所定義的 interface
HRESULT __stdcall DllGetClassObject(REFCLSID rclsid,
REFIID riid,
LPVOID *ppv);
呼叫完 CoCreateInstance 時, 理論上應該就可以獲得所需要介面的 pointer 進而操作此
COM 物件. 另外當我們使用 Microsoft 所提供的開發工具建立 COM 專案時, 上述的
DllGetClassObject 我們將不用自己實作, 在 COM 已經完成這些事情了, 我們只需要依照步
驟建立所需要的介面即可.
訂閱:
文章 (Atom)