這幾天公司有一個(gè)新的需求,就是要將我們開(kāi)發(fā)的某些東西都抽取出來(lái)晒喷,打個(gè)比方我們開(kāi)發(fā)了A項(xiàng)目,可以以后B項(xiàng)目要包含A項(xiàng)目的功能访敌,C項(xiàng)目也有可能包含A項(xiàng)目的功能凉敲。研究了幾種實(shí)現(xiàn)方式還是考慮用靜態(tài)庫(kù)的方式,將要重復(fù)實(shí)現(xiàn)的功能打包成靜態(tài)庫(kù)寺旺。
iOS靜態(tài)庫(kù)打包網(wǎng)上的教程很多爷抓,但是我感覺(jué)最詳細(xì),最清晰的還是這個(gè)網(wǎng)站阻塑,它包括靜態(tài)庫(kù)制作以及bundle資源文件打包蓝撇,大家可以參考以下:
如果你想將你開(kāi)發(fā)的控件與別人分享,一種方法是直接提供源代碼文件陈莽。然而渤昌,這種方法并不是很優(yōu)雅虽抄。它會(huì)暴露所有的實(shí)現(xiàn)細(xì)節(jié),而這些實(shí)現(xiàn)你可能并不想開(kāi)源出來(lái)独柑。此外迈窟,開(kāi)發(fā)者也可能并不想看到你的所有代碼,因?yàn)樗麄兛赡軆H僅希望將你的這份漂亮代碼的一部分植入自己的應(yīng)用中忌栅。
另一種方法是將你的代碼編譯成靜態(tài)庫(kù)(library)车酣,讓其他開(kāi)發(fā)者添加到自己的項(xiàng)目中。然而狂秘,這需要你一并公布所有的公開(kāi)的頭文件骇径,實(shí)在是非常不方便躯肌。
你需要一種簡(jiǎn)單的方法來(lái)編譯你的代碼者春,這種方法應(yīng)該使得你的代碼易分享,并且在多個(gè)工程中易復(fù)用清女。你需要的是一種方法來(lái)打包你的靜態(tài)庫(kù)钱烟,將所有的頭文件放到一個(gè)單元中,這樣你就可以立刻將其加入到你的項(xiàng)目中并使用嫡丙。
非常幸運(yùn)拴袭,這正是本篇教程所要解決的問(wèn)題。你將會(huì)學(xué)到制作并使用Framework曙博,幫助你解決這個(gè)頭疼的問(wèn)題拥刻。OS X完美地支持這一點(diǎn),因?yàn)閄code就提供了一個(gè)項(xiàng)目模板父泳,包含著默認(rèn)構(gòu)建目標(biāo)(target)和可以容納類(lèi)似于圖片般哼、聲音、字體等資源的文件惠窄。你可以為iOS創(chuàng)建Framework蒸眠,不過(guò)這是一個(gè)比較復(fù)雜的手工活,如果你跟著教程走杆融,你將學(xué)到怎么樣跨過(guò)路障楞卡,順利地完成Framework的創(chuàng)建。
當(dāng)你跟著這篇教程走完后脾歇,你將能夠:
使用Xcode構(gòu)建一個(gè)基本的靜態(tài)庫(kù)工程蒋腮。
依賴(lài)于該靜態(tài)庫(kù)工程構(gòu)建一款應(yīng)用。
掌握如何將靜態(tài)庫(kù)工程轉(zhuǎn)換為完整的藕各、合格的Framework徽惋。
最終,你將看到如何將一個(gè)圖像文件同F(xiàn)ramework一起打包到resource bundle下座韵。
開(kāi)始
這篇教程的主要目的是解釋怎么樣在你的iOS工程中創(chuàng)建并使用一個(gè)Framework险绘。所以踢京,不像其他網(wǎng)站上的教程,這篇教程將只使用一小部分Objective-C代碼宦棺,并且這一小部分主要是為了說(shuō)明我們將會(huì)遇到的一些概念瓣距。
從這里下載可用的資源文件RWKnobControl。如果你在Creating a Static Library Project 這篇文章中完成了創(chuàng)建第一個(gè)項(xiàng)目的過(guò)程代咸,這里你將會(huì)看到怎么樣使用去它們蹈丸。
在創(chuàng)建本工程時(shí),你將要?jiǎng)?chuàng)建的所有的代碼和項(xiàng)目文件都可以在Github上找到(https://github.com/sammyd/RW-iOSFramework)呐芥。對(duì)于本篇教程中每個(gè)創(chuàng)建階段都有不同的commit逻杖。
什么是Framework?
Framework是資源的集合思瘟,將靜態(tài)庫(kù)和其頭文件包含到一個(gè)結(jié)構(gòu)中荸百,讓Xcode可以方便地把它納入到你的項(xiàng)目中。
在OS X上滨攻,可能會(huì)創(chuàng)建一個(gè)動(dòng)態(tài)連接(Dynamically Linked)的framework够话。通過(guò)動(dòng)態(tài)連接,framework可以更新光绕,不需要應(yīng)用重新連接女嘲。在運(yùn)行時(shí),庫(kù)中代碼的一份拷貝被分享出來(lái)诞帐,整個(gè)工程都可以使用它欣尼,因此,這樣減少了內(nèi)存消耗停蕉,提高了系統(tǒng)的性能愕鼓。正如你看到的,這是一個(gè)功能強(qiáng)大的特性谷徙。
在iOS上拒啰,你不能用這種方式添加為系統(tǒng)添加自定義的framework,因此僅有的動(dòng)態(tài)鏈接的framework只能是Apple提供的那些完慧。(編者注:在iOS 8中已加入此特性谋旦,開(kāi)發(fā)者可以使用第三方的動(dòng)態(tài)框架)
然而,這并不意味著framework對(duì)于iOS而言是無(wú)關(guān)緊要的屈尼,靜態(tài)連接的framework依然可以打包代碼册着,使其在不同的應(yīng)用中復(fù)用。
由于framework本質(zhì)上是靜態(tài)庫(kù)的“一站式采購(gòu)點(diǎn)”脾歧,因此在本篇教程中你所做的第一件事就是創(chuàng)建并使用靜態(tài)庫(kù)甲捏。當(dāng)跟著教程走到如何創(chuàng)建framework時(shí),你就能明白你所做的一切了鞭执,整體思路也不會(huì)那么煙霧繚繞了司顿。
創(chuàng)建一個(gè)靜態(tài)庫(kù)工程
打開(kāi)Xcode芒粹,點(diǎn)擊File\New\Project,選擇iOS\Framework and Library\Cocoa Touch Static Library新建一個(gè)靜態(tài)庫(kù)工程.
將工程命名為RWUIControls大溜,然后將工程保存到一個(gè)空目錄下化漆。
一個(gè)靜態(tài)庫(kù)工程由頭文件和實(shí)現(xiàn)文件組成,這些文件將被編譯為庫(kù)本身钦奋。
為了方便其他開(kāi)發(fā)者使用你的庫(kù)和framework座云,你將進(jìn)行一些操作,讓他們僅需要導(dǎo)入一個(gè)頭文件便可以訪問(wèn)所有你想公開(kāi)的類(lèi)付材。
當(dāng)創(chuàng)建靜態(tài)庫(kù)工程時(shí)朦拖,Xcode會(huì)自動(dòng)添加RWUIControls.h和RWUIControls.m。你不需要實(shí)現(xiàn)文件厌衔,因此右鍵單擊RWUIControls.m選擇delete璧帝,將它刪除到廢紙簍中。
打開(kāi)RWUIControls.h葵诈,將所有內(nèi)容替換為:
1
#import < UIKit/UIKit.h>
導(dǎo)入U(xiǎn)IKit的頭文件裸弦,這是創(chuàng)建一個(gè)庫(kù)所需要的祟同。當(dāng)你在創(chuàng)建不同的組成類(lèi)時(shí)作喘,你將會(huì)將它們添加到這個(gè)文件中,確保它們能夠被庫(kù)的使用者獲取到晕城。
你所構(gòu)建的項(xiàng)目依賴(lài)于UIKit泞坦,然而Xcode的靜態(tài)庫(kù)工程不會(huì)自動(dòng)連接到UIKit。要解決這個(gè)問(wèn)題砖顷,就要將UIKit作為依賴(lài)庫(kù)添加到工程中贰锁。在工程導(dǎo)航欄中選擇工程名,然后在中央面板中選擇RWUIControls目標(biāo)滤蝠。
點(diǎn)擊BuildPhases豌熄,展開(kāi)Link Binary with Libraries這一部分,點(diǎn)擊+添加一個(gè)新的framework物咳,找到UIKit.framework锣险,點(diǎn)擊add添加進(jìn)來(lái)。
如果不結(jié)合頭文件览闰,靜態(tài)庫(kù)是沒(méi)有用的芯肤,靜態(tài)庫(kù)編譯一組文件,在這些文件中類(lèi)和方法都以二進(jìn)制數(shù)據(jù)的形式存在压鉴。在你創(chuàng)建的庫(kù)中崖咨,有些類(lèi)將能夠被公開(kāi)訪問(wèn)到,有些類(lèi)只能由庫(kù)內(nèi)部訪問(wèn)并使用油吭。
接下來(lái)击蹲,你需要在build欄中添加新的phase署拟,來(lái)包含所有頭文件,并將它們放到編譯器可以獲取到的某個(gè)地方歌豺。然后芯丧,你將會(huì)拷貝這些到你的framework中。
依然是在Xcode的Build Phases界面世曾,選擇Editor\Add Build Phase\Add Copy Headers Build Phase缨恒。
Note:如果你發(fā)現(xiàn)按上面找到的菜單項(xiàng)是灰色的(不可點(diǎn)擊的),點(diǎn)擊下方Build Phases界面的白色區(qū)域來(lái)獲取Xcode的應(yīng)用焦點(diǎn)轮听,然后重新試一下骗露。
把RWUIControls.h從項(xiàng)目導(dǎo)航欄中拖到中央面板的Copy Headers下的Public部分。這一步確保任何使用你的庫(kù)的用戶(hù)均可以獲取該頭文件血巍。
Note:顯然萧锉,所有包含在你的公共頭文件中的頭文件必須是對(duì)外公開(kāi)的,這一點(diǎn)非常重要述寡。否則柿隙,開(kāi)發(fā)者在使用你的庫(kù)時(shí)會(huì)得到編譯錯(cuò)誤。如果Xcode在讀取公共頭文件時(shí)不能讀到你忘記設(shè)為public的頭文件鲫凶,這實(shí)在是太令人沮喪了禀崖。
創(chuàng)建一個(gè)UI控件
既然你已經(jīng)設(shè)置好你的工程了,是時(shí)候?yàn)槟愕膸?kù)添加一些功能了螟炫。由于本篇教程的關(guān)鍵在于教你怎么樣創(chuàng)建一個(gè)framework波附,而不是怎么樣構(gòu)建一個(gè)UI控件,這里你將使用上一篇教程中創(chuàng)建好的控件昼钻。在你之前下載好的壓縮包文件中找到RWKnobControl目錄掸屡,從Finder中拖到Xcode下RWUIControls目錄下。
選擇Copy items into destination group’s folder然评,點(diǎn)擊下方的選擇框仅财,確保RWUIControls靜態(tài)庫(kù)目標(biāo)被選中。
這一步默認(rèn)把實(shí)現(xiàn)文件添加到編譯列表碗淌,把頭文件添加到Project組盏求。這意味著它們目前是私有的。
Note:在你弄清楚之前贯莺,這三個(gè)組的名稱(chēng)可能會(huì)讓你迷惑风喇,Public是你期望的,Private下的頭文件依然是可以暴露出來(lái)的缕探,因此名字可能有些誤導(dǎo)魂莫。諷刺的是,在Project下的頭文件對(duì)你的工程來(lái)說(shuō)才是“私有”的爹耗,因此耙考,你將會(huì)更多地希望你的頭文件或者在Public下谜喊,或者在Project下。
現(xiàn)在倦始,你需要將控件的頭文件RWKnobControl.h分享出來(lái)斗遏,有幾種方式可以實(shí)現(xiàn)這一點(diǎn),首先是在Copy Headers面板中將這個(gè)頭文件從Project欄拖到Public欄鞋邑。
或者诵次,你可能會(huì)發(fā)現(xiàn),更簡(jiǎn)單的方法是枚碗,編輯文件逾一,改變Target Membership面板下的membership。這個(gè)選項(xiàng)更方便一些肮雨,可以讓你不斷添加文件遵堵,擴(kuò)充你的庫(kù)。
![](http://cc.cocimg.com/api/uploads/20150127/1422338022618092.png)
Note:如果你不斷往庫(kù)中添加新的類(lèi)怨规,記得及時(shí)更新這些類(lèi)的關(guān)系(membership)陌宿,使盡可能少的類(lèi)成為public,并確保其他非public的頭文件都在Project下波丰。
對(duì)你的控件的頭文件需要做的另一件事是將其添加到庫(kù)的主頭文件RWControls.h中壳坪。在這個(gè)主頭文件的幫助下,開(kāi)發(fā)者使用你的庫(kù)僅僅需要導(dǎo)入一個(gè)頭文件呀舔,如下面的代碼一樣弥虐,而不是自己去選擇自己需要的一塊導(dǎo)入扩灯。
1
#import < RWUIControls/RWUIControls.h>
因此媚赖,在RWUIControls.h中添加下面的代碼:
1
2
// Knob Control
#import
配置Build Settings
現(xiàn)在距離構(gòu)建這個(gè)項(xiàng)目、創(chuàng)建靜態(tài)庫(kù)已經(jīng)非常接近了珠插。不過(guò)惧磺,這里要先進(jìn)行一些配置,讓我們的庫(kù)對(duì)于用戶(hù)來(lái)說(shuō)更友好捻撑。
首先磨隘,你需要提供一個(gè)目錄名,表示你將把拷貝的公共頭文件存放到哪里顾患。這樣確保當(dāng)你使用靜態(tài)庫(kù)的時(shí)候可以定位到相關(guān)頭文件的位置番捂。
在項(xiàng)目導(dǎo)航欄中點(diǎn)擊項(xiàng)目名,然后選擇RWUIControls靜態(tài)庫(kù)目標(biāo)江解,選擇Build Setting欄设预,然后搜索public header,雙擊Public Headers Folder Path犁河,在彈出視圖中鍵入如圖所示內(nèi)容:
一會(huì)你就會(huì)看到這個(gè)目錄了鳖枕。
現(xiàn)在你需要改變一些其他的設(shè)置魄梯,尤其是那些在二進(jìn)制庫(kù)中遺留下的設(shè)置,編譯器提供給你一個(gè)選項(xiàng)宾符,來(lái)消除無(wú)效代碼:永遠(yuǎn)不會(huì)被執(zhí)行的代碼酿秸。當(dāng)然你也可以移除掉一些debug用符號(hào),例如某些函數(shù)名稱(chēng)或者其他跟debug相關(guān)的細(xì)節(jié)魏烫。
因?yàn)槟阏趧?chuàng)建framework供他人使用辣苏,最好禁掉這些功能(無(wú)效代碼和debug用符號(hào)),讓用戶(hù)自己選擇對(duì)自己的項(xiàng)目有利的部分使用哄褒。和之前一樣考润,使用搜索框,改變下述設(shè)置:
Dead Code Stripping設(shè)置為NO
Strip Debug Symbol During Copy 全部設(shè)置為NO
Strip Style設(shè)置為Non-Global Symbols
編譯然后運(yùn)行读处,到目前為止沒(méi)什么可看的糊治,不過(guò)確保項(xiàng)目可以成功構(gòu)建,沒(méi)有錯(cuò)誤和警報(bào)是非常好的罚舱。
選擇目標(biāo)為iOS Device井辜,按下command + B進(jìn)行編譯,一旦成功管闷,工程導(dǎo)航欄中Product目錄下libRWUIControls.a文件將從紅色變?yōu)楹谏嘟牛砻鳜F(xiàn)在該文件已經(jīng)存在了。右鍵單擊libRWUIControls.a包个,選擇Show in Finder刷允。
再此目錄下,你將看到靜態(tài)庫(kù)碧囊,libRWUIControls.a树灶,以及其他你為頭文件指定的目錄。注意到糯而,正如你所期望的天通,那些定為public的頭文件可以在此看到。
創(chuàng)建一個(gè)依賴(lài)開(kāi)發(fā)(Dependent Development)工程
在無(wú)法看到真實(shí)效果的情況下為iOS開(kāi)發(fā)一個(gè)UI控件庫(kù)是極其困難的熄驼,而這是我們現(xiàn)在面臨的問(wèn)題像寒。
沒(méi)有人期望你閉著眼睛開(kāi)發(fā)出一個(gè)UI控件,因此在這一部分你將創(chuàng)建一個(gè)新的Xcode工程瓜贾,該工程依賴(lài)于你剛剛創(chuàng)建好的庫(kù)诺祸。這意味著允許你使用示例app創(chuàng)建一個(gè)framework。當(dāng)然祭芦,這部分代碼將和庫(kù)本身完全分離筷笨,結(jié)構(gòu)會(huì)非常清晰。
選擇File\Close Project關(guān)閉之前的靜態(tài)庫(kù)工程,使用File\New\Project創(chuàng)建一個(gè)新的工程奥秆,選擇iOS\Application\Single View Application逊彭,將新工程命名為UIControlDevApp,將類(lèi)前綴命名為RW构订,選擇該工程只支持iPhone侮叮,最后將項(xiàng)目保存到和之前的RWUIControls相同的目錄下。
添加RWUIControls依賴(lài)庫(kù)悼瘾,將RWUIControls.xcodeproj從Finder中拖到Xcode中UIControlDevApp組下囊榜。
現(xiàn)在你可以在你的工程中導(dǎo)航到庫(kù)工程了,這樣做非常好亥宿,因?yàn)檫@樣意味著你可以在庫(kù)中編輯代碼卸勺,并且運(yùn)行示例工程來(lái)測(cè)試你做的改變。
Note:你無(wú)法將同一工程在兩個(gè)Xcode窗口中同時(shí)打開(kāi)烫扼,如果你發(fā)現(xiàn)你無(wú)法在你的工程中導(dǎo)航到庫(kù)工程的話(huà)曙求,檢查一下是否庫(kù)工程在其他Xcode窗口中打開(kāi)了。
這里你可以拷貝代碼映企,而不是和上一個(gè)教程似的重新創(chuàng)建代碼悟狱。首先,選擇Main.storyboard, RWViewController.h 和 RWViewController.m堰氓,然后右鍵單擊挤渐,選擇Delete,將它們刪除到廢紙簍中双絮。然后浴麻,將你之前下載的壓縮文件中DevApp文件夾拷貝到Xcode的UIControlDevApp組下。
現(xiàn)在囤攀,你將添加靜態(tài)庫(kù)作為實(shí)例項(xiàng)目的依賴(lài)庫(kù):
在項(xiàng)目導(dǎo)航欄中選擇UIControlDevApp软免。
導(dǎo)航到UIControlDevApp目標(biāo)下Build Phases面板下。
打開(kāi)Target Dependencies面板抚岗,點(diǎn)擊+按鈕調(diào)出選擇器或杠。
找到RWUIControls靜態(tài)庫(kù),選擇并點(diǎn)擊Add宣蔚。這一步表明當(dāng)構(gòu)建dev應(yīng)用時(shí),Xcode會(huì)檢查是否靜態(tài)庫(kù)需要重新構(gòu)建认境。
為了連接到靜態(tài)庫(kù)本身胚委,展開(kāi)Link Binary With Libraries面板,再次點(diǎn)擊+按鈕叉信,從Workspace組中選擇libRWUIControls.a然后點(diǎn)擊Add亩冬。
這一步確保Xcode可以連接到靜態(tài)庫(kù),就像連接到系統(tǒng)framework(例如UIKit)一樣。
編譯并運(yùn)行硅急,如果你按照之前的教程創(chuàng)建了一個(gè)旋鈕控件覆享,在你眼前展示的將是與之相同的應(yīng)用。
像這樣使用嵌套工程的好處是你可以對(duì)庫(kù)本身做出修改营袜,而不用離開(kāi)示例工程,即使你同時(shí)改變兩個(gè)地方的代碼也一樣。每次你編譯工程佳谦,你都要檢查是否將頭文件的public/project關(guān)系設(shè)置正確队寇。如果實(shí)例工程中缺失了任何需要的頭文件,它都不能被編譯跪另。
創(chuàng)建一個(gè)Framework
到現(xiàn)在拧抖,你可能迫不及待地點(diǎn)著腳趾頭,想著什么時(shí)候framework可以出來(lái)免绿∵笙可以理解,因?yàn)榈浆F(xiàn)在為止你已經(jīng)做了許多工作嘲驾,然而卻沒(méi)有看到過(guò)framework的身影袱吆。
現(xiàn)在該有所改變了,你之所以到現(xiàn)在都沒(méi)有創(chuàng)建一個(gè)framework距淫,是因?yàn)閒ramework本身就是靜態(tài)庫(kù)加上一組頭文件——實(shí)際上正是你已經(jīng)創(chuàng)建好的東西绞绒。
當(dāng)然,framework也有幾點(diǎn)不同之處:
目錄結(jié)構(gòu)榕暇。Framework有一個(gè)能被Xcode識(shí)別的特殊的目錄結(jié)構(gòu)蓬衡,你將會(huì)創(chuàng)建一個(gè)build task,由它來(lái)為你創(chuàng)建這種結(jié)構(gòu)彤枢。
片段(Slice)狰晚。目前為止,當(dāng)你構(gòu)建庫(kù)時(shí)缴啡,僅僅考慮到當(dāng)前需要的結(jié)構(gòu)(architecture)壁晒。例如,i386业栅、arm7等秒咐,為了讓一個(gè)framework更有用,對(duì)于每一個(gè)運(yùn)行framework的結(jié)構(gòu)碘裕,該framework都需要構(gòu)建這種結(jié)構(gòu)携取。一會(huì)你就會(huì)創(chuàng)建一個(gè)新的工程,構(gòu)建所有需要的結(jié)構(gòu)帮孔,并將它們包含到framework中雷滋。
這一部分非常神奇,不過(guò)我們會(huì)慢慢地來(lái)。實(shí)際上它并不像看起來(lái)那樣復(fù)雜晤斩。
Framework結(jié)構(gòu)
正如之前提到的焕檬,一個(gè)framework有一個(gè)特殊的目錄結(jié)構(gòu),看起來(lái)像是這樣的:
現(xiàn)在你需要在靜態(tài)庫(kù)構(gòu)建過(guò)程中添加腳本來(lái)創(chuàng)建這種結(jié)構(gòu)澳泵,在項(xiàng)目導(dǎo)航欄中選擇RWUIControls实愚,然后選擇RWUIControls靜態(tài)庫(kù)目標(biāo),選擇Build Phases欄烹俗,然后選擇Editor/Add Build Phase/Add Run Script Build Phase來(lái)添加一個(gè)新的腳本爆侣。
這一步在build phases部分添加了一個(gè)新的面板,這允許你在構(gòu)建時(shí)運(yùn)行一個(gè)Bash腳本幢妄。你希望讓腳本在build的過(guò)程中何時(shí)執(zhí)行兔仰,就把這個(gè)面板拖動(dòng)到列表中相對(duì)應(yīng)的那一位置。對(duì)于該framework工程來(lái)說(shuō)蕉鸳,腳本最后執(zhí)行乎赴,因此你可以讓它保留在默認(rèn)的位置即可。
雙擊面板標(biāo)題欄Run Script潮尝,重命名為Build Framework榕吼。
在腳本文本框中粘貼下面的Bash腳本代碼
set -e
export FRAMEWORK_LOCN="${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework"
# Create the path to the real Headers die
mkdir -p "${FRAMEWORK_LOCN}/Versions/A/Headers"
# Create the required symlinks
/bin/ln -sfh A "${FRAMEWORK_LOCN}/Versions/Current"
/bin/ln -sfh Versions/Current/Headers "${FRAMEWORK_LOCN}/Headers"
/bin/ln -sfh "Versions/Current/${PRODUCT_NAME}" \
"${FRAMEWORK_LOCN}/${PRODUCT_NAME}"
# Copy the public headers into the framework
/bin/cp -a "${TARGET_BUILD_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}/" \
"${FRAMEWORK_LOCN}/Versions/A/Headers"
這個(gè)腳本首先創(chuàng)建了RWUIControls.framework/Versions/A/Headers目錄,然后創(chuàng)建了一個(gè)framework所需要的三個(gè)連接符號(hào)(symbolic links)勉失。
Versions/Current => A
Headers => Versions/Current/Headers
RWUIControls => Versions/Current/RWUIControls
最后羹蚣,將公共頭文件從你之前定義的公共頭文件路徑拷貝到Versions/A/Headers目錄下,-a參數(shù)確保修飾次數(shù)作為拷貝的一部分不會(huì)改變乱凿,防止不必要的重新編譯顽素。
現(xiàn)在,選擇RWUIControls靜態(tài)庫(kù)scheme徒蟆,然后選擇iOS Device構(gòu)建目標(biāo)胁出,然后使用cmd+B構(gòu)建。在RWUIControls工程里Products目錄下右鍵單擊libRWUIControls.a靜態(tài)庫(kù)段审,然后再一次選擇Show in Finder全蝶。
在這次構(gòu)建目錄中你可以看到RWUIControls.framework,可以確定一下這里展示了正確的目錄結(jié)構(gòu):
這算是在完成你的framework的過(guò)程中邁出了一大步寺枉。不過(guò)你會(huì)注意到這里并沒(méi)有一個(gè)靜態(tài)lib文件抑淫。這就是我們下一步將要解決的問(wèn)題。
多架構(gòu)(Multi-Architecture)編譯
iOS app需要在許多不同的CPU架構(gòu)下運(yùn)行:
arm7: 在最老的支持iOS7的設(shè)備上使用
arm7s: 在iPhone5和5C上使用
arm64: 運(yùn)行于iPhone5S的64位 ARM 處理器 上
i386: 32位模擬器上使用
x86_64: 64為模擬器上使用
每個(gè)CPU架構(gòu)都需要不同的二進(jìn)制數(shù)據(jù)型凳,當(dāng)你編譯一個(gè)應(yīng)用時(shí)丈冬,無(wú)論你目前正在使用那種架構(gòu),Xcode都會(huì)正確地依照對(duì)應(yīng)的架構(gòu)編譯甘畅。例如,如果你想跑在虛擬機(jī)上,Xcode只會(huì)編譯i386版本(或者是64位機(jī)的x86_64版本)疏唾。
這意味著編譯會(huì)盡可能快地進(jìn)行蓄氧,當(dāng)你歸檔一款app或者構(gòu)建app的發(fā)布版本(release mode)時(shí),Xcode會(huì)構(gòu)建上述三個(gè)用于真機(jī)的ARM架構(gòu)槐脏。因此這樣app就可以跑在所有設(shè)備上了喉童。不過(guò),其他的編譯架構(gòu)又如何呢顿天?
當(dāng)你創(chuàng)建你的framework時(shí)堂氯,你自然會(huì)想讓所有開(kāi)發(fā)者都能在所有可能的架構(gòu)上運(yùn)行它,不是嗎牌废?你當(dāng)然想咽白,因?yàn)檫@樣可以從同行那兒得到尊敬與贊美。
因此你需要讓Xcode在所有架構(gòu)下都進(jìn)行編譯鸟缕。這一過(guò)程實(shí)際上是創(chuàng)建了二進(jìn)制FAT(File Allocation Table晶框,文件配置表),它包含了所有架構(gòu)的片段(slice)懂从。
Note:這里實(shí)際上強(qiáng)調(diào)了創(chuàng)建依賴(lài)靜態(tài)庫(kù)的示例項(xiàng)目的另一個(gè)原因:庫(kù)僅僅在示例項(xiàng)目運(yùn)行所需要的架構(gòu)下編譯授段,只有當(dāng)有變化的時(shí)候才重新編譯,為什么這一點(diǎn)會(huì)讓人激動(dòng)番甩?因?yàn)殚_(kāi)發(fā)周期會(huì)盡可能地縮短侵贵。
這里將使用在RWUIControls工程中的一個(gè)新的目標(biāo)來(lái)構(gòu)建framework,在項(xiàng)目導(dǎo)航欄中選擇RWUIControls缘薛,然后點(diǎn)擊已經(jīng)存在的目標(biāo)下面的Add Target按鈕窍育。
找到iOS/Other/Aggregate,點(diǎn)擊Next掩宜,將目標(biāo)命名為Framework蔫骂。
Note:為什么使用集合(Aggregate)目標(biāo)來(lái)創(chuàng)建一個(gè)framework呢?為什么這么不直接牺汤?因?yàn)镺S X對(duì)庫(kù)的支持更好一些辽旋,事實(shí)上,Xcode直接為每一個(gè)OS X工程提供一個(gè)Cocoa Framework編譯目標(biāo)檐迟〔古撸基于此,你將使用集合編譯目標(biāo)追迟,作為Bash腳本的連接串來(lái)創(chuàng)建神奇的framework目錄結(jié)構(gòu)溶其。你是不是開(kāi)始覺(jué)得這里的方法有些愚蠢了?
為了確保每當(dāng)這個(gè)新的framework目標(biāo)被創(chuàng)建時(shí)敦间,靜態(tài)鏈接庫(kù)都會(huì)被編譯瓶逃,你需要往靜態(tài)庫(kù)目標(biāo)中添加依賴(lài)(Dependency)束铭。在庫(kù)工程中選擇Framework目標(biāo),在Build Phases中添加一個(gè)依賴(lài)厢绝。展開(kāi)Target Dependencies面板契沫,點(diǎn)擊 + 按鈕選擇RWUIControls靜態(tài)庫(kù)。
這個(gè)目標(biāo)的主要編譯部分是多平臺(tái)編譯昔汉,你將使用一個(gè)腳本來(lái)做到這一點(diǎn)懈万。和你之前做的一樣,在Framework目標(biāo)下靶病,選擇Build Phases欄会通,點(diǎn)擊Editor/Add Build Phase/Add Run Script Build Phase,創(chuàng)建一個(gè)新的Run Script Build Phase娄周。
雙擊Run Script涕侈,重命名腳本的名字。這次命名為MultiPlatform Build昆咽。
在腳本文本框中粘貼下面的Bash腳本代碼:
set -e
# If we're already inside this script then die
if [ -n "$RW_MULTIPLATFORM_BUILD_IN_PROGRESS" ]; then
exit 0
fi
export RW_MULTIPLATFORM_BUILD_IN_PROGRESS=1
RW_FRAMEWORK_NAME=${PROJECT_NAME}
RW_INPUT_STATIC_LIB="lib${PROJECT_NAME}.a"
RW_FRAMEWORK_LOCATION="${BUILT_PRODUCTS_DIR}/${RW_FRAMEWORK_NAME}.framework"
set –e確保腳本的任何地方執(zhí)行失敗驾凶,則整個(gè)腳本都執(zhí)行失敗。這樣可以避免讓你創(chuàng)建一個(gè)部分有效的framework掷酗。
接著调违,用RW_MULTIPLATFORM_BUILD_IN_PROGRESS變量決定是否循環(huán)調(diào)用腳本,如果有該變量泻轰,則退出技肩。
然后設(shè)定一些變量。該framework的名字與項(xiàng)目的名字一樣浮声。也就是RWUIControls虚婿,另外,靜態(tài)lib的名字是libRWUIControls.a泳挥。
接下來(lái)然痊,用腳本設(shè)置一些函數(shù),這些函數(shù)一會(huì)項(xiàng)目就會(huì)用到屉符,把下面的代碼加到腳本的底部剧浸。
function build_static_library {
# Will rebuild the static library as specified
#? ? build_static_library sdk
xcrun xcodebuild -project "${PROJECT_FILE_PATH}" \
-target "${TARGET_NAME}" \
-configuration "${CONFIGURATION}" \
-sdk "${1}" \
ONLY_ACTIVE_ARCH=NO \
BUILD_DIR="${BUILD_DIR}" \
OBJROOT="${OBJROOT}" \
BUILD_ROOT="${BUILD_ROOT}" \
SYMROOT="${SYMROOT}" $ACTION
}
function make_fat_library {
# Will smash 2 static libs together
#? ? make_fat_library in1 in2 out
xcrun lipo -create "${1}" "${2}" -output "${3}"
}
build_static_library把SDK作為參數(shù),例如iPhone7.0矗钟,然后創(chuàng)建靜態(tài)lib唆香,大多數(shù)參數(shù)直接傳到當(dāng)前的構(gòu)建工作中來(lái),不同的是設(shè)置ONLY_ACTIVE_ARCH來(lái)確保為當(dāng)前SDK構(gòu)建所有的結(jié)構(gòu)吨艇。
make_fat_library使用lipo將兩個(gè)靜態(tài)庫(kù)合并為一個(gè)躬它,其參數(shù)為兩個(gè)靜態(tài)庫(kù)和結(jié)果的輸出位置。從這里了解更多關(guān)于lipo的知識(shí)东涡。
為了使用這兩個(gè)方法冯吓,接下來(lái)腳本將定義更多你要用到的變量倘待,你需要知道其他SDK是什么,例如桑谍,iphoneos7.0應(yīng)該對(duì)應(yīng)iphonesimulator7.0延柠,反過(guò)來(lái)也一樣祸挪。你也需要找到該SDK對(duì)應(yīng)的編譯目錄锣披。
把下面的代碼添加到腳本的底部。
# 1 - Extract the platform (iphoneos/iphonesimulator) from the SDK name
if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]]; then
RW_SDK_PLATFORM=${BASH_REMATCH[1]}
else
echo "Could not find platform name from SDK_NAME: $SDK_NAME"
exit 1
fi
# 2 - Extract the version from the SDK
if [[ "$SDK_NAME" =~ ([0-9]+.*$) ]]; then
RW_SDK_VERSION=${BASH_REMATCH[1]}
else
echo "Could not find sdk version from SDK_NAME: $SDK_NAME"
exit 1
fi
# 3 - Determine the other platform
if [ "$RW_SDK_PLATFORM" == "iphoneos" ]; then
RW_OTHER_PLATFORM=iphonesimulator
else
RW_OTHER_PLATFORM=iphoneos
fi
# 4 - Find the build directory
if [[ "$BUILT_PRODUCTS_DIR" =~ (.*)$RW_SDK_PLATFORM$ ]]; then
RW_OTHER_BUILT_PRODUCTS_DIR="${BASH_REMATCH[1]}${RW_OTHER_PLATFORM}"
else
echo "Could not find other platform build directory."
exit 1
fi
上面四句聲明都非常相似贿条,都是使用字符串比較和正則表達(dá)式來(lái)確定RW_OTHER_PLATFORM和RW_OTHER_BUILT_PRODUCTS_DIR雹仿。
詳細(xì)解釋一下上面四句聲明:
SDK_NAME將指代iphoneos7.0和iphonesimulator6.1,這個(gè)正則表達(dá)式取出字符串開(kāi)頭不包含數(shù)字的那些字符整以,因此胧辽,其結(jié)果是iphoneos 或 iphonesimulator。
這個(gè)正則表達(dá)式取出SDK_NAME中表示版本用的數(shù)字公黑,7.0或6.1等邑商。
這里用簡(jiǎn)單的字符串比較來(lái)將iphonesimulator 轉(zhuǎn)換為iphoneos,反過(guò)來(lái)也一樣凡蚜。
從構(gòu)建好的工程的目錄路徑的末尾找出平臺(tái)名稱(chēng)人断,將其替換為其他平臺(tái)。這樣可以確保為其他平臺(tái)構(gòu)建的目錄可以被找到朝蜘。這是將兩個(gè)靜態(tài)庫(kù)合并的關(guān)鍵部分恶迈。
現(xiàn)在你可以啟動(dòng)腳本為其他平臺(tái)編譯,然后得到合并兩靜態(tài)庫(kù)的結(jié)果谱醇。
在腳本最后添加下面的代碼:
# Build the other platform.
build_static_library "${RW_OTHER_PLATFORM}${RW_SDK_VERSION}"
# If we're currently building for iphonesimulator, then need to rebuild
#? to ensure that we get both i386 and x86_64
if [ "$RW_SDK_PLATFORM" == "iphonesimulator" ]; then
build_static_library "${SDK_NAME}"
fi
# Join the 2 static libs into 1 and push into the .framework
make_fat_library "${BUILT_PRODUCTS_DIR}/${RW_INPUT_STATIC_LIB}" \
"${RW_OTHER_BUILT_PRODUCTS_DIR}/${RW_INPUT_STATIC_LIB}" \
"${RW_FRAMEWORK_LOCATION}/Versions/A/${RW_FRAMEWORK_NAME}"
首先暇仲,調(diào)用你之前定義好的函數(shù)為其他平臺(tái)編譯
如果你現(xiàn)在正在為模擬器編譯,那么Xcode會(huì)默認(rèn)只在該系統(tǒng)對(duì)應(yīng)的結(jié)構(gòu)下編譯副渴,例如i386 或 x86_64奈附。為了在這兩個(gè)結(jié)構(gòu)下都進(jìn)行編譯,這里調(diào)用了build_static_library煮剧,基于iphonesimulator SDK重新編譯斥滤,確保這兩個(gè)結(jié)構(gòu)都進(jìn)行了編譯。
最后調(diào)用make_fat_library將在當(dāng)前編譯目錄下的靜態(tài)lib同在其他目錄下地lib合并轿秧,依次實(shí)現(xiàn)支持多結(jié)構(gòu)的FAT靜態(tài)庫(kù)中跌。這個(gè)被放到了framework中。
腳本的最后是簡(jiǎn)單的拷貝命令菇篡,將下面代碼添加到腳本最后:
# Ensure that the framework is present in both platform's build directories
cp -a "${RW_FRAMEWORK_LOCATION}/Versions/A/${RW_FRAMEWORK_NAME}" \
"${RW_OTHER_BUILT_PRODUCTS_DIR}/${RW_FRAMEWORK_NAME}.framework/Versions/A/${RW_FRAMEWORK_NAME}"
# Copy the framework to the user's desktop
ditto "${RW_FRAMEWORK_LOCATION}" "${HOME}/Desktop/${RW_FRAMEWORK_NAME}.framework"
第一條命令確保framework在所有平臺(tái)的編譯目錄中都存在漩符。
第二條將完成的framework拷貝到用戶(hù)的桌面上,這一步是可選的驱还,但我發(fā)現(xiàn)這樣做可以很方便的存取framework嗜暴。
選擇Framework集合方案(aggregate scheme)凸克,按下cmd+B編譯該framework。
這一步將構(gòu)建并在你的桌面上存放一個(gè)RWUIControls.framework闷沥。
為了檢查一下我們的多平臺(tái)編譯真的成功了萎战,啟動(dòng)終端,導(dǎo)航到桌面上的framework舆逃,像下面一樣:
$ cd ~/Desktop/RWUIControls.framework
$ RWUIControls.framework? xcrun lipo -info RWUIControls
第一條指令導(dǎo)航到framework中蚂维,第二行使用lipo指令從RWUIControls靜態(tài)庫(kù)中得到需要的信息,這將列出存在于該庫(kù)中的所有片段路狮。
![](http://cc.cocimg.com/api/uploads/20150127/1422340121883866.png)
這里你可以看到虫啥,一共有五種片段:i386, x86_64, arm7, arm7s 和 arm64,正如你在編譯時(shí)設(shè)定的那樣奄妨。如果你之前使用lipo –info指令涂籽,你可以看到這些片段的一個(gè)分組。
如何使用Framework砸抛?
OK评雌,你已經(jīng)有了framework,你也有了庫(kù)直焙。它們可以提供一種優(yōu)雅的方法來(lái)解決你迄今為止還沒(méi)有遇到過(guò)的問(wèn)題景东,但是做這些的意義是什么呢?
使用framework的其中一個(gè)主要的優(yōu)點(diǎn)是簡(jiǎn)化使用箕般,現(xiàn)在你將創(chuàng)建一個(gè)簡(jiǎn)單的iOS應(yīng)用耐薯,并使用你剛剛創(chuàng)建好的RWUIControls.framework。
使用Xcode創(chuàng)建一個(gè)新工程丝里,選擇File/New/Project曲初,然后選擇iOS/Application/Single View Application,將新工程命名為ImageViewer杯聚,設(shè)置為僅僅用于iPhone臼婆,將其保存到與之前兩個(gè)工程同樣的目錄下。這個(gè)應(yīng)用將展示一張圖片幌绍,允許用戶(hù)使用RWKnobControl旋轉(zhuǎn)圖片颁褂。
在你之前下載的壓縮文件中找到ImageViewer目錄,這里面只有一個(gè)圖片文件傀广,把這個(gè)圖片文件sampleImage.jpg從Finder中拖到Xcode的ImageViewer組中颁独。
選中Copy items into destination group’s folder,點(diǎn)擊Finish完成導(dǎo)入操作伪冰。
導(dǎo)入一個(gè)framework的步驟幾乎相同誓酒,將RWUIControls.framework從桌面拖到Xcode中的Frameworks組下。同樣贮聂,確保選中了Copy items into destination group’s folder靠柑。
打開(kāi)RWViewController.m寨辩,將里面的代碼替換為下面的代碼:
#import "RWViewController.h"
#import < RWUIControls/RWUIControls.h>
@interface RWViewController ()
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) RWKnobControl *rotationKnob;
@end
@implementation RWViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Create UIImageView
CGRect frame = self.view.bounds;
frame.size.height *= 2/3.0;
self.imageView = [[UIImageView alloc] initWithFrame:CGRectInset(frame, 0, 20)];
self.imageView.image = [UIImage imageNamed:@"sampleImage.jpg"];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:self.imageView];
// Create RWKnobControl
frame.origin.y += frame.size.height;
frame.size.height /= 2;
frame.size.width? = frame.size.height;
self.rotationKnob = [[RWKnobControl alloc] initWithFrame:CGRectInset(frame, 10, 10)];
CGPoint center = self.rotationKnob.center;
center.x = CGRectGetMidX(self.view.bounds);
self.rotationKnob.center = center;
[self.view addSubview:self.rotationKnob];
// Set up config on RWKnobControl
self.rotationKnob.minimumValue = -M_PI_4;
self.rotationKnob.maximumValue = M_PI_4;
[self.rotationKnob addTarget:self
action:@selector(rotationAngleChanged:)
forControlEvents:UIControlEventValueChanged];
}
- (void)rotationAngleChanged:(id)sender
{
self.imageView.transform = CGAffineTransformMakeRotation(self.rotationKnob.value);
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
@end
這就是一個(gè)簡(jiǎn)單的視圖控制器,它做了以下幾件事:
使用#import導(dǎo)入框架的頭文件
設(shè)置了一組私有屬性來(lái)持有UIImageView和RWKnobControl歼冰。
創(chuàng)建一個(gè)UIImageView靡狞,將其放到合適的位置。
為Knob control設(shè)置一些屬性隔嫡,包括添加值改變的事件監(jiān)聽(tīng)器甸怕。相應(yīng)方法為rotationAngleChanged:方法。
rotationAngleChanged:方法簡(jiǎn)單更新了UIImageView的transform屬性畔勤,讓圖片隨著knob control的移動(dòng)而旋轉(zhuǎn)蕾各。
具體怎么樣使用RWKnobControl,可以看一下上一篇教程庆揪,那里解釋了怎么樣去創(chuàng)建它。
編譯并運(yùn)行妨托,你就能看到一款簡(jiǎn)單的應(yīng)用缸榛,當(dāng)你改變knob control的值時(shí)圖片就會(huì)旋轉(zhuǎn)。
打包(Bundle)資源
你有沒(méi)有注意到RWUIControls的framework只包含了代碼和頭文件兰伤,其他的文件卻沒(méi)有被包含内颗。例如,你沒(méi)有使用其他任何資源敦腔,比如圖片均澳。這是iOS的一個(gè)限制,framework只能包含頭文件和靜態(tài)庫(kù)符衔。
現(xiàn)在準(zhǔn)備好找前,這篇教程要開(kāi)始進(jìn)階了。這一部分你將學(xué)到怎么樣通過(guò)使用bundle整合資源判族,讓其可以隨著framework一起發(fā)布躺盛,進(jìn)而突破這一限制。
你將創(chuàng)建一個(gè)新的UI控件——絲帶控件形帮,作為RWUIControls庫(kù)的一部分槽惫。這個(gè)控件將在一個(gè)UIView的右上方展示一個(gè)絲帶圖片。
創(chuàng)建一個(gè)Bundle
資源都會(huì)被添加到bundle中辩撑,這將是RWUIControls工程上的另一個(gè)目標(biāo)界斜。
打開(kāi)UIControlDevApp工程,選擇RWUIControls子工程合冀,點(diǎn)擊Add Target按鈕各薇,導(dǎo)航到OS X/Framework and Library/Bundle。將新的Bundle命名為RWUIControlsResources水慨,然后從framework選擇框中選擇Core Foundation得糜。
這里需要配置幾個(gè)編譯設(shè)置敬扛,因?yàn)槟阏趧?chuàng)建一個(gè)在iOS上使用的bundle,這與默認(rèn)的OS X不同朝抖。選擇RWUIControlsResources目標(biāo)啥箭,然后點(diǎn)擊Build Settings欄,搜索base sdk治宣,選擇Base SDK這一行急侥,按下delete鍵,這一步將OS X切換為iOS侮邀。
同時(shí)你需要將工程名稱(chēng)改為RWUIControls坏怪。搜索product name,雙擊進(jìn)入編輯模式绊茧,將${TARGET_NAME}替換為RWUIControls铝宵。
默認(rèn)情況下,有兩種resolutions的圖片可以產(chǎn)生一些有趣的現(xiàn)象华畏。例如鹏秋,當(dāng)你導(dǎo)入一個(gè)retina @2x版本的圖片時(shí),普通版的和Retina版的將會(huì)合并成一個(gè)多resolution的TIFF(標(biāo)簽圖像文件格式亡笑,Tagged Image File Format)侣夷。這不是一件好事。搜索hidpi將COMBINE_HIDPI_IMAGES設(shè)置為NO仑乌。
現(xiàn)在百拓,你將確保當(dāng)你編譯framework時(shí),bundle也能被編譯并將framework作為依賴(lài)添加到集體目標(biāo)中晰甚。選中Framework目標(biāo)衙传,選擇Build Phases欄,展開(kāi)Target Dependencies面板压汪,點(diǎn)擊 + 按鈕粪牲,選擇RWUIControlsResources目標(biāo)將其添加為依賴(lài)。
現(xiàn)在止剖,在Framework目標(biāo)的Build Phases中腺阳,打開(kāi)MultiPlatform Build面板,在腳本的最后添加下述代碼:
# Copy the resources bundle to the user's desktop
ditto "${BUILT_PRODUCTS_DIR}/${RW_FRAMEWORK_NAME}.bundle" \
"${HOME}/Desktop/${RW_FRAMEWORK_NAME}.bundle"
這條指令將拷貝構(gòu)建好的bundle到用戶(hù)的桌面上〈┫悖現(xiàn)在亭引,編譯framework scheme,你會(huì)發(fā)現(xiàn)bundle在桌面上出現(xiàn)皮获。
導(dǎo)入Bundle
為了用這個(gè)新的bundle開(kāi)發(fā)焙蚓,你需要在示例項(xiàng)目中使用它,這意味著你必須既把它作為依賴(lài)添加到工程中,同時(shí)作為一個(gè)對(duì)象拷貝到項(xiàng)目中购公。
在項(xiàng)目導(dǎo)航欄中萌京,選擇UIControlDevApp工程,點(diǎn)擊UIControlDevApp目標(biāo)宏浩,展開(kāi)RWUIControls工程的Product組知残,把RWUIControls.bundle拖到Copy Bundle Resources面板中的 Build Phases欄。
在Target Dependencies面板中比庄,點(diǎn)擊+按鈕求妹,添加新的依賴(lài),然后選擇RWUIControlsResources佳窑。
創(chuàng)建一個(gè)絲帶視圖(Ribbon View)
上面的就是所有必需的配置工作了制恍,從你之前下載的壓縮文件中將RWRibbon文件夾拖入到RWUIControls工程下RWUIControls組中。
選中Copy the items into the destination group’s folder神凑,在對(duì)應(yīng)的選擇框中打勾净神,確保它被添加到RWUIControls靜態(tài)lib目標(biāo)中。
代碼中一個(gè)很重要的部分是你怎樣引用一張圖片耙厚。如果你看一下RWRibbonView.m文件中的addRibbonView方法强挫,你將會(huì)看到相關(guān)的這一行代碼:
1
UIImage *image = [UIImage imageNamed:@"RWUIControls.bundle/RWRibbon"];
Bundle就像一個(gè)文件目錄,所以引用bundle中的一張圖片是非常簡(jiǎn)單的薛躬。
將圖片添加到bundle中,選擇這張圖片呆细,在右邊的面板中型宝,通過(guò)選擇來(lái)表示它應(yīng)該屬于RWUIControlsResources目標(biāo)。
還記得我們說(shuō)過(guò)要確保framework可以被訪問(wèn)嗎絮爷?現(xiàn)在趴酣,你需要導(dǎo)出頭文件RWRibbon.h,在Target Membership面板中選擇該文件坑夯,然后從彈出視圖中選擇Public岖寞。
最后,你需要將頭文件引用添加到framework的頭文件中柜蜈。打開(kāi)RWUIControls.h添加下面這兩行:
// RWRibbon
#import < RWUIControls/RWRibbonView.h>
將絲帶添加到示例工程中
在UIControlDevApp項(xiàng)目中打開(kāi)RWViewController.m文件仗谆,在@interface后的大括號(hào)中添加下面的實(shí)例變量聲明。
1
RWRibbonView? *_ribbonView;
在viewDidLoad:的末尾添加下面的代碼來(lái)創(chuàng)建一個(gè)絲帶視圖:
// Creates a sample ribbon view
_ribbonView = [[RWRibbonView alloc] initWithFrame:self.ribbonViewContainer.bounds];
[self.ribbonViewContainer addSubview:_ribbonView];
// Need to check that it actually works :)
UIView *sampleView = [[UIView alloc] initWithFrame:_ribbonView.bounds];
sampleView.backgroundColor = [UIColor lightGrayColor];
[_ribbonView addSubview:sampleView];
編譯并運(yùn)行UIControlDevApp scheme淑履。你將看到新的絲帶控件出現(xiàn)在應(yīng)用的下方隶垮。
在ImageViewer中使用Bundle
我要向你分享的最后一件事是怎么樣在其他應(yīng)用中使用這個(gè)新的bundle,例如秘噪,你之前創(chuàng)建的ImageViewer應(yīng)用狸吞。
開(kāi)始之前,確保你的bundle和framework都是最新版本的,選擇Framework scheme然后按下cmd+B編譯蹋偏。
打開(kāi)ImageViewer工程便斥,找到Frameworks組中的RWUIControls.framework項(xiàng)目,然后將其刪除威始,選擇Move to Trash枢纠。然后將RWUIControls.framework從你的桌面上拖到Frameworks組中。這是必須的字逗,因?yàn)榇藭r(shí)的framework已經(jīng)與你第一次導(dǎo)入時(shí)的framework大不相同了京郑。
Note:如果Xcode拒絕讓你添加framework,這可能是因?yàn)槟悴](méi)有真正將之前版本的framework刪除到廢紙簍葫掉。如果是因?yàn)檫@樣的話(huà)些举,從Finder中ImageViewer目錄下刪除framework然后重新嘗試。
導(dǎo)入bundle俭厚,簡(jiǎn)單將其從桌面上拖到ImageViewer組中户魏。選中Copy items into destination group’s folder,選中對(duì)應(yīng)的選擇框挪挤,確保它被添加到ImageViewer目標(biāo)中叼丑。
接下來(lái)你要將絲帶添加到可以旋轉(zhuǎn)的圖片上。因此扛门,在RWViewController.m文件中代碼要有一些簡(jiǎn)單的變動(dòng)鸠信。
打開(kāi)該文件,將屬性imageView的類(lèi)型從UIImageView變?yōu)镽WRibbonView:
1
@property (nonatomic, strong) RWRibbonView *imageView;
將viewDidLoad方法中第一部分论寨,負(fù)責(zé)創(chuàng)建并配置UIImageView的代碼星立,替換為下面的代碼:
[super viewDidLoad];
// Create UIImageView
CGRect frame = self.view.bounds;
frame.size.height *= 2/3.0;
self.imageView = [[RWRibbonView alloc] initWithFrame:CGRectInset(frame, 0, 20)];
UIImageView *iv = [[UIImageView alloc] initWithFrame:self.imageView.bounds];
iv.image = [UIImage imageNamed:@"sampleImage.jpg"];
iv.contentMode = UIViewContentModeScaleAspectFit;
[self.imageView addSubview:iv];
[self.view addSubview:self.imageView];
編譯并運(yùn)行該項(xiàng)目,現(xiàn)在該項(xiàng)目中你同時(shí)使用了RWUIControls framework下的RWKnobControl和RWRibbonView葬凳。
現(xiàn)在該干什么绰垂?
在本篇教程中,你學(xué)到了關(guān)于創(chuàng)建一個(gè)framework并在你的iOS app中使用所需的一切知識(shí)火焰,包括開(kāi)發(fā)一個(gè)framework的最好的方式劲装,以及怎么樣使用bundle來(lái)共享資源。
有沒(méi)有一個(gè)你喜歡的功能在多個(gè)app中使用了呢昌简?現(xiàn)在你所學(xué)到的概念可以幫你創(chuàng)建一個(gè)可復(fù)用的庫(kù)占业,使你的編碼更加簡(jiǎn)單。Framework提供了一種優(yōu)雅的方式來(lái)獲得庫(kù)中的代碼江场,讓你在寫(xiě)一個(gè)炫酷的app的時(shí)候纺酸,可以靈活地獲取到你需要的一切。
完整工程的源碼被放到了
(https://github.com/sammyd/RW-iOSFramework)
上址否,每一步都有一個(gè)commit餐蔬∷槲桑或者你可以從這里下載完整的壓縮文件。
以上引用自:http://www.cocoachina.com/ios/20150127/11022.html
但是這個(gè)網(wǎng)站靜態(tài)庫(kù)打包只說(shuō)了一種情況樊诺,就是工程中都是.h .m 文件仗考,引用的第三方類(lèi)庫(kù)也是將第三方的.h .m 文件都添加到工程中了。但是我們?cè)趯?shí)際開(kāi)發(fā)過(guò)程中很可能我的工程引用的第三方是第三方庫(kù).framework ,如果是這種情況的話(huà)词爬,打包就要稍作區(qū)分了秃嗜,大家可以參考這篇博客:
http://www.reibang.com/p/155a3cfb765e?utm_campaign=hugo?
主要的區(qū)別在這,導(dǎo)入第三方不要勾選target
但是樓樓跟著這篇博客的教程還是沒(méi)成功顿膨,但我做成靜態(tài)庫(kù)其它地方引用的時(shí)候出錯(cuò)了锅锨,因?yàn)檫@里要特別注意的是,當(dāng)我們的靜態(tài)庫(kù)有引用其它的靜態(tài)庫(kù)恋沃,或者依賴(lài)系統(tǒng)的某些類(lèi)庫(kù)的時(shí)候我們一點(diǎn)要在工程的 Bulid Phases- Link Binary Libraries 下手動(dòng)將這些依賴(lài)的靜態(tài)庫(kù)以及靜態(tài)庫(kù)所依賴(lài)的系統(tǒng)庫(kù)都手動(dòng)導(dǎo)入進(jìn)去必搞,這樣編譯才不會(huì)出錯(cuò)。最好自己建幾個(gè)demo試一試囊咏。