你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Qt for iOS,Qt 與Objective C混合編程

Qt for iOS,Qt 與Objective C混合編程

編輯:IOS開發基礎

最近有好幾個使用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,問搜索引擎去。因為我所知實在有限,怕誤導了您。當然如果您不怕,往下看吧。

OC源文件介紹

首先我要說一下 Objective C 的源文件,後綴是.m 或 .mm ,在 .mm 文件裡,可以直接使用 C++ 代碼。所以,我們要混合 Qt 代碼與 OC 代碼,就需要在 Qt 項目裡加入 mm 文件。

pro 文件配置

Qt SDK for Mac ,安裝之後, Qt Creator 會使用 XCode 提供的編譯工具鏈來編譯代碼,能夠正確編譯 mm 文件,也可以鏈接 iOS 的庫文件。

而要混合 OC 代碼,需要更改一下 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 文件中與混合使用 OC 相關的配置項,大體就這些了。接下來我們看如何寫 Objective C 代碼啦。

混合使用Objective C 代碼

乖乖,很惶恐啊,這是我的弱項,沒寫過多少 OC 代碼。所以,請不要問我 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 文件內包含 OC 的頭文件,與 C++ 頭文件一個理兒,不過要使用 #import 哦。類似醬紫:

#import < UIKit/UIKit.h>
#import < GLKit/GLKit.h>

包含了頭文件,就可以使用 OC 類庫了。比如我要在 QQuickView 上面創建一個新的 iOS 原生的 UIView ,.mm 文件裡可以這樣:

void addOCView(QQuickWindow *w)
{
    UIView *view = reinterpret_cast(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(view->winId());
    uiw = [[UIWindow alloc] initWithFrame:CGRectMake(x, y, width, height)];
    [v addSubview: uiw];

上面代碼中,view 是 QQuickView 。這次我們創建 UIView 時使用了傳入的 x 、 y 、 width 、 height,這樣新建的 UIView 就和 QML 元素整合在一起了,看起來好像是一體的。

OK,這就是全部了。

(本文作者:foruok)

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved