Qt 與 Objective-C 的混合程式設計
最近有好幾個使用Qt的朋友問起 Qt for iOS 的事情,因為我在這方面的經驗特別少,寫不出系統的文章來,非常抱歉,不能給出令人滿意的答覆,推薦大家去看 Jason’s Home ,在我部落格左側邊欄的友情連結裡也有,他提供了 Qt for iOS 的一些非常有意義的文章,而且是基於實踐的,他的 App 已經在 App Store 中上線。
至於我呢,在這篇文章裡,簡單介紹一些如何混合 Qt 與 OC 程式設計。
我要說的內容呢,大部分在 Qt 幫助裡都有,請大家到索引模式下,鍵入”Qt for iOS”,找到 Qt for iOS 這篇文章來看。它介紹了搭建開發環境、編譯應用、混合OC程式設計這三個方面,已經非常詳細了。
如果你不想啃英文,那可以接著我的文章往下看。
專案設定
既然要聊 Qt 混合 OC 程式設計,首先要簡單介紹一下 Objective-C 。我只有一句話:Go,問搜尋引擎去。因為我所知實在有限,怕誤導了您。當然如果您不怕,往下看吧。
Objective-C原始檔介紹
首先我要說一下 Objective-C 的原始檔,字尾是.m 或 .mm ,在 .mm 檔案裡,可以直接使用 C++ 程式碼。所以,我們要混合 Qt 程式碼與 Objective-C 程式碼,就需要在 Qt 專案里加入 mm 檔案。
pro 檔案配置
Qt SDK for Mac ,安裝之後, Qt Creator 會使用 XCode 提供的編譯工具鏈來編譯程式碼,能夠正確編譯 mm 檔案,也可以連結 iOS 的庫檔案。
而要混合 Objective-C 程式碼,需要更改一下 pro 檔案。一個是新增 mm 檔案,一個是連線針對 iOS 的庫檔案。
新增原始檔,使用 OBJECTIVE_SOURCES 這個變數,比如這樣子:
OBJECTIVE_SOURCES += ocview.mm
連結庫 XCode 提供的庫,則需要使用 QMAKE_LFLAGS ,類似這樣子:
ios { QMAKE_LFLAGS += -framework OpenGLES QMAKE_LFLAGS += -framework GLKit QMAKE_LFLAGS += -framework QuartzCore QMAKE_LFLAGS += -framework CoreVideo QMAKE_LFLAGS += -framework CoreAudio QMAKE_LFLAGS += -framework CoreImage QMAKE_LFLAGS += -framework CoreMedia QMAKE_LFLAGS += -framework AVFoundation QMAKE_LFLAGS += -framework AudioToolbox QMAKE_LFLAGS += -framework CoreGraphics QMAKE_LFLAGS += -framework UIKit }
上面是我使用 Qt 針對 iOS 程式設計的配置。我使用了很多針對 iOS 的庫,所以新增了很多 framework 。
“ -framework UIKit ”這種引數,是經由 Makefile 傳遞給 Clang 的引數,-framework 是用來指示要連結某個框架(或者說庫)的關鍵字,它後面跟的是框架(庫)名。
需要注意的是,我們使用針對 iOS 的庫,不是通過“ LIBS += ”這種方式來引入哦。當然,你自己通過 Qt 實現的 .a 庫,依然需要使用“ LIBS += ”這種方式。
指定plist檔案
有時你需要為你的專案指定 plist 檔案, plist 檔案全名是 Property List ,字尾是 .plist 。它用來定義 iOS 應用的屬性,比如 Bundle(iOS上的一個應用被稱為一個 Bundle ) 的顯示名字、可執行檔名字、簽名、證書等等,當然也可以儲存一些配置資料。具體的介紹參考 iOS 開發的文件吧。
要在 pro 檔案裡新增 plist ,要使用 QMAKE_INFO_PLIST 關鍵字。如下面這樣子:
QMAKE_INFO_PLIST += MultiWindow.plist
好啦,關於 pro 檔案中與混合使用Objective-C 相關的配置項,大體就這些了。接下來我們看如何寫 Objective C 程式碼啦。
混合使用Objective C 程式碼
乖乖,很惶恐啊,這是我的弱項,沒寫過多少 Objective-C 程式碼。所以,請不要問我 OC 有關的問題,我真不知道……
背景
我的示例,是在 QML 的介面上疊加iOS原生的介面,即 UIView、UIWindow之類的。因為 OC 是 C 的近親,和 C++ 有著天然的血緣,混合起來特別方便哈,比 Android 上使用 JNI 程式設計好用多了。
不過有一點, OC 都適用 [] 這種語法來呼叫函式,使用 XCode 的話,語法提示和自動完成功能非常強大,基本不用思考的就能找到你要用的函式。而 Qt Creator 麼,嘿嘿,就沒這麼好相與了,純粹要手寫哦。我當時都是開著 XCode 看 API 文件找的,比較痛苦。
QQuickView 是什麼
我測試時的示例,用的是 Qt Quick App 專案模板,使用 QQuickView 來載入 QML 文件。這裡也以此為例來說明。
首先要說 QQuickView 到底是什麼。
QQuickView 呢,其實是一個 UIView 。UIView 則是 iOS 開發框架裡很多介面元素的根兒,比如 UIWindow 就是 UIView 的子類。
Qt 的 QQuickView 是一個 UIView ,建立了 QQuickView 例項後,就有了一個 UIView ,然後 Qt 玩了一些魔法,拿到了 UIView 的 OpenGL Context ,跑起了 Qt 的事件迴圈,在這個 OpenGL Context 上從零開始繪製了自己的場景和 UI 系統。
就這麼簡單,你可以查閱 Qt 原始碼來進一步瞭解。
需要注意的是, QML 介面元素的渲染,與 UIView 這種原生介面的渲染,不在一個執行緒中。而且 iOS 對 OpenGL ES 的支援很好,你可以同時使用多個 OpenGL Context 。更好的是,你可以視窗模式來建立一個 OpenGL Context 。不像 Android 版本哦, Qt 使用 OpenGL 的時候都是全屏模式,區域性更新不支援,所以我們在 Android 上使用 QML 裡的 Camera 和 VideoOutput 來開發拍照應用時, VideoOutput 必須是全屏模式(必須fill_parent)。而在 iOS 上,則沒有這個限制了。看來 iOS 還是很美好的啦。
我靠,扯得有點兒遠,最近寫技術文章少了,越來越羅嗦了。言歸正傳吧。
因為 QQuickView 實際上就是一個 UIView ,所以可以強制轉換為 UIView ,然後使用 OC 的方法來建立新的 UIView 或者 UIWindow ,這樣就有了原生的 UI 元件了,你可以在這個原生的 UI 元件上使用 OpenGL 繪製自己的東西或者新增其它原生的控制元件,非常美好。
不過要說明的是,通過這種方法建立出來的 iOS 原生介面元素,會始終在 QML 介面之上,把 QML 場景裡的介面元素給蓋住。
混合程式碼
要使用 OC 的類庫,需要在 mm 檔案內包含相關的標頭檔案,又有幾部分工作要做。一個是在 pro 檔案里加入 SDK 路徑,使用 INCLUDEPATH 變數即可,不多說了。另外一點是在 mm 檔案內包含 Objective-C 的標頭檔案,與 C++ 標頭檔案一個理兒,不過要使用 #import 哦。類似醬紫:
#import <UIKit/UIKit.h> #import <GLKit/GLKit.h>
包含了標頭檔案,就可以使用 Objective-C 類庫了。比如我要在 QQuickView 上面建立一個新的 iOS 原生的 UIView ,.mm 檔案裡可以這樣:
void addOCView(QQuickWindow *w) { UIView *view = reinterpret_cast<UIView *>(w->winId()); CGRect viewRect = CGRectMake(10, 10, 100, 100); UIView* myView = [[UIView alloc] initWithFrame:viewRect]; [myView setBackgroundColor:[UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0]]; [view addSubview: myView]; }
如你所見,我寫了一個 addOCView 方法,它的引數是 QQuickView 。在 addOCView 方法裡,我把 QQuickView 強制轉換為 UIView 來使用。
我建立了一個新的 UIView ,設定了它的背景顏色,然後把它新增為 QQuickView 的子視窗。諾,就這麼簡單了。
說下 main.cpp ,看它如何使用 addOCView() 方法。程式碼如下:
int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQuickView viewer; viewer.setResizeMode(QQuickView::SizeRootObjectToView); viewer.setSource(QUrl("qrc:/main.qml")); viewer.show(); addOCView(&viewer); return app.exec(); }
一點兒都不驚喜是吧,就是直接呼叫了 addOCView 哦。哈哈,確實如此了。
iOS 原生介面與 QML 元素的位置對映
混合使用 iOS 原生介面時,也可以達到原生介面與 QML 介面的無縫整合。關鍵就在於計算 QML 介面元素的位置,然後根據 QML 介面元素的位置來設定原生介面的位置。
QML元素位置換算
QML 提供了換算元素位置的方法,Item 有個方法,叫作 mapToItem() ,它可以把一個相對於 Qt Quick Item 的區域對映到另一個 Item 上,得到座標了。比如你的 qml 檔案是下面的樣子:
Rectangle { ... Rectangle { id: videoLayer; anchors.margins: 8; anchors.left: parent.left; anchors.right: parent.right; anchors.top: parent.top; anchors.bottom: actionBar.top; color: "green"; } ... }
你想把原生的 UIView 定位到 id 為 videoLayer 的元素內部,可以類似下面換算一個座標並使用它:
var coordinate = videoLayer.mapToItem(null, 8, 8, videoLayer.width - 16, videoLayer.height - 16); winUtil.addUIView(coordinate.x, coordinate.y, coordinate.width, coordinate.height);
換算出的coordinate有 x 、 y 、 width 、 height 屬性。當 mapToItem 的第一個引數為 null 時,換算的結果就是相對於 QQuickView 的。那我們在新增 UIView 時,就可以用這個換算結果來構造一個 CGRect 物件,用這個 CGRect 來初始化 UIView 的位置。
設定 UIView 的位置
前面示例程式碼中的 winUtil 是我在 C++ 內實現的一個輔助類,匯出到了 QML 環境中。它的 addUIView 方法根據傳入的座標來設定原生 UIView 的位置。參考程式碼如下:
UIView *v = reinterpret_cast<UIView*>(view->winId()); uiw = [[UIWindow alloc] initWithFrame:CGRectMake(x, y, width, height)]; [v addSubview: uiw];
上面程式碼中,view 是 QQuickView 。這次我們建立 UIView 時使用了傳入的 x 、 y 、 width 、 height,這樣新建的 UIView 就和 QML 元素整合在一起了,看起來好像是一體的。
OK,這就是全部了。
相關文章
- Swift和Objective-C混合程式設計——Swift呼叫OCSwiftObject程式設計
- Swift和Objective-C混合程式設計——OC呼叫SwiftSwiftObject程式設計
- XCode 中 Swift / Objective-C / C / C++ 混合程式設計XCodeSwiftObjectC++程式設計
- Java與Matlab混合程式設計JavaMatlab程式設計
- C與指令碼的混合程式設計 (轉)指令碼程式設計
- 託管與非託管的混合程式設計程式設計
- 基於Python與Qt的快速GUI程式設計PythonQTGUI程式設計
- C/C++與Matlab混合程式設計初探C++Matlab程式設計
- C++:與C混合程式設計 CMake undefined reference toC++程式設計Undefined
- CUDA 8的混合精度程式設計程式設計
- QT QML模組的程式設計挑戰與解決方案QT程式設計
- QT程式設計------VS2012配置QT環境,QT入門程式設計QT程式設計
- VB與VC混合程式設計中處理訊息的方法 (轉)程式設計
- 【IDL】 IDL與C#混合程式設計技術C#程式設計
- QT QML模組的程式設計藝術QT程式設計
- QT皮膚(QSS)程式設計QT程式設計
- ZT QT網路程式設計QT程式設計
- QML之C++混合程式設計C++程式設計
- Groovy + Java 混合程式設計方案:GMavenJava程式設計Maven
- C++ & Intel MKL 混合程式設計C++Intel程式設計
- 【混合程式設計】C/C++呼叫Fortran的DLL程式設計C++
- C/C++在Java、Android和Objective-C三大平臺下實現混合程式設計C++JavaAndroidObject程式設計
- Android混合程式設計:WebView實踐Android程式設計WebView
- Qt 中Socket程式設計例項QT程式設計
- Qt程式設計之悲慘世界QT程式設計
- qt model view 程式設計總結QTView程式設計
- Qt程式設計師必看/關於Qt收費的官方答覆QT程式設計師
- Matlab & C++ 混合程式設計mex檔案的編寫與除錯MatlabC++程式設計除錯
- Objective-C——實現物件導向程式設計Object物件程式設計
- 零基礎學Qt4程式設計:Qt4開發入門與提升QT程式設計
- FFT原理及C++與MATLAB混合程式設計詳細介紹FFTC++Matlab程式設計
- Objective-C 執行時程式設計指南-介紹Object程式設計
- Objective-C 2.0 執行時系統程式設計Object程式設計
- OC/Swift/C/C++混合使用的程式設計姿勢SwiftC++程式設計
- 學習 Qt 程式設計的好書精品推薦!QT程式設計
- Qt Creator程式設計之正規表示式QT程式設計
- QT程式設計之——使用全域性變數QT程式設計變數
- Qt程式設計獲取滑鼠移動事件QT程式設計事件