iOS開發——創建Framework(Xcode9)

這篇教程的主要目的是怎么樣在你的iOS工程中創建并使用一個Framework。

Framework是資源的集合,將靜態庫和其頭文件包含到一個結構中,讓Xcode可以方便地把它納入到你的項目中。

在OS X上,可能會創建一個動態連接(Dynamically Linked)的framework。通過動態連接,framework可以更新,不需要應用重新連接。在運行時,庫中代碼的一份拷貝被分享出來,整個工程都可以使用它,因此,這樣減少了內存消耗,提高了系統的性能。正如你看到的,這是一個功能強大的特性。

在iOS上,你不能用這種方式添加為系統添加自定義的framework,因此僅有的動態鏈接的framework只能是Apple提供的那些。(編者注:在iOS 8中已加入此特性,開發者可以使用第三方的動態框架)

然而,這并不意味著framework對于iOS而言是無關緊要的,靜態連接的framework依然可以打包代碼,使其在不同的應用中復用。

framework本質上是靜態庫的“一站式采購點”,本篇教程中你所做的第一件事就是創建并使用靜態庫。

創建一個靜態庫工程

打開Xcode,點擊File\New\Project,選擇iOS\Framework and Library\Cocoa Touch Static Library新建一個靜態庫工程.

圖片.png

將工程命名為MYSDK,然后將工程保存到一個空目錄下。

圖片.png

一個靜態庫工程由頭文件和實現文件組成,這些文件將被編譯為庫本身。

為了方便其他開發者使用你的庫和framework,你將進行一些操作,讓他們僅需要導入一個頭文件便可以訪問所有你想公開的類。

當創建靜態庫工程時,Xcode會自動添加RWUIControls.h和MYSDK.m。你不需要實現文件,因此右鍵單擊MYSDK.m選擇delete,將它刪除到廢紙簍中。

打開MYSDK.h,將所有內容替換為:

import < UIKit/UIKit.h>

導入UIKit的頭文件,這是創建一個庫所需要的。當你在創建不同的組成類時,你將會將它們添加到這個文件中,確保它們能夠被庫的使用者獲取到。

點擊BuildPhases,展開Link Binary with Libraries這一部分,點擊+添加一個新的framework,找到UIKit.framework,點擊add添加進來。

接下來,你需要在build欄中添加新的phase,來包含所有頭文件,并將它們放到編譯器可以獲取到的某個地方。然后,你將會拷貝這些到你的framework中。

依然是在Xcode的Build Phases界面,選擇Editor\Add Build Phase\Add Headers Build Phase。

圖片.png

Note:如果你發現按上面找到的菜單項是灰色的(不可點擊的),點擊下方Build Phases界面的白色區域來獲取Xcode的應用焦點,然后重新試一下。

創建你的SDK代碼(可以是UI,也可以是工具類等)

既然你已經設置好你的工程了,是時候為你的庫添加一些功能了。由于本篇教程的關鍵在于教你怎么樣創建一個framework,而不是怎么樣構建一個UI控件,這里你將使用上一篇教程中創建好的控件。將你需要打包成Framework的代碼,從Finder中拖到Xcode下MYSDK目錄下。

選擇Copy items into destination group’s folder,點擊下方的選擇框,確保MYSDK靜態庫目標被選中。

圖片.png

這一步系統默認把實現文件添加到編譯列表,把頭文件添加到Project組。意味著它們目前是私有的。

Public是你期望暴露出來的,但Private下的頭文件依然是可以暴露出來的,名字可能有些誤導。在Project下的頭文件對你的工程來說才是“私有”的,因此,我們的頭文件最好在Public下,或者在Project下。

現在,你需要將要暴露的頭文件.h分享出來,有幾種方式可以實現這一點,首先是在Copy Headers面板中將這個頭文件從Project欄拖到Public欄。

圖片.png

還有一個方法,編輯文件,改變Target Membership面板下的membership。

圖片.png

使盡可能少的類成為public,并確保其他非public的頭文件都在Project下。

對你的控件的頭文件需要做的另一件事是將其添加到庫的主頭文件RWControls.h中。在這個主頭文件的幫助下,開發者使用你的庫僅僅需要導入一個頭文件,如下面的代碼一樣,而不是自己去選擇自己需要的一塊導入。

import < MYSDK/MYSDK.h>

因此,在MYSDK.h中添加下面的代碼:

圖片.png

這里要先進行一些配置,讓我們的庫對于用戶來說更友好。

首先,你需要提供一個目錄名,表示你將把拷貝的公共頭文件存放到哪里。這樣確保當你使用靜態庫的時候可以定位到相關頭文件的位置。

在項目導航欄中點擊項目名,然后選擇RWUIControls靜態庫目標,選擇Build Setting欄,然后搜索public header,雙擊Public Headers Folder Path,在彈出視圖中鍵入include/$(PROJECT_NAME) :

圖片.png

一會你就會看到這個目錄了。

現在你需要改變一些其他的設置,尤其是那些在二進制庫中遺留下的設置,編譯器提供給你一個選項,來消除無效代碼:永遠不會被執行的代碼。當然你也可以移除掉一些debug用符號,例如某些函數名稱或者其他跟debug相關的細節。

因為你正在創建framework供他人使用,最好禁掉這些功能(無效代碼和debug用符號),讓用戶自己選擇對自己的項目有利的部分使用。和之前一樣,使用搜索框,改變下述設置:

Dead Code Stripping設置為NO

Strip Debug Symbol During Copy 全部設置為NO

Strip Style設置為Non-Global Symbols

編譯然后運行,到目前為止沒什么可看的,不過確保項目可以成功構建,沒有錯誤和警報是非常好的。

選擇目標為iOS Device,按下command + B進行編譯,一旦成功,工程導航欄中Product目錄下MYSDK.a文件將從紅色變為黑色,表明現在該文件已經存在了。右鍵單擊MYSDK.a,選擇Show in Finder。

圖片.png

再此目錄下,你將看到靜態庫,MYSDK.a,以及其他你為頭文件指定的目錄。注意到,正如你所期望的,那些定為public的頭文件可以在此看到。

創建一個依賴開發(Dependent Development)工程

在無法看到真實效果的情況下為iOS開發一個UI控件庫是極其困難的,而這是我們現在面臨的問題。

沒有人期望你閉著眼睛開發出一個UI控件,因此在這一部分你將創建一個新的Xcode工程,該工程依賴于你剛剛創建好的庫。這意味著允許你使用示例app創建一個framework。當然,這部分代碼將和庫本身完全分離,結構會非常清晰。

選擇File\Close Project關閉之前的靜態庫工程,使用File\New\Project創建一個新的工程,選擇iOS\Application\Single View App,將新工程命名為MYSDKDemo,將類前綴命名為RW,選擇該工程只支持iPhone,最后將項目保存到和之前的RWUIControls相同的目錄下。

添加MYSDK依賴庫,將MYSDK.xcodeproj從Finder中拖到Xcode中MYSDKDemo組下。

圖片.png

現在你可以在你的工程中導航到庫工程了,你可以在庫中編輯代碼,并且運行示例工程來測試你做的改變。

Note:你無法將同一工程在兩個Xcode窗口中同時打開,如果你發現你無法在你的工程中導航到庫工程的話,檢查一下是否庫工程在其他Xcode窗口中打開了。

現在,你將添加靜態庫作為實例項目的依賴庫:

在項目導航欄中選擇MYSDKDemo。

導航到MYSDKDemo目標下Build Phases面板下。

打開Target Dependencies面板,點擊+按鈕調出選擇器。

找到MYSDK靜態庫,選擇并點擊Add。這一步表明當構建dev應用時,Xcode會檢查是否靜態庫需要重新構建。

為了連接到靜態庫本身,展開Link Binary With Libraries面板,再次點擊+按鈕,從Workspace組中選擇MYSDK.a然后點擊Add。

這一步確保Xcode可以連接到靜態庫,就像連接到系統framework(例如UIKit)一樣。

圖片.png

編譯并運行,展現的是你自己編輯的界面。

圖片.png

像這樣使用嵌套工程的好處是你可以對庫本身做出修改,而不用離開示例工程,即使你同時改變兩個地方的代碼也一樣。每次你編譯工程,你都要檢查是否將頭文件的public/project關系設置正確。如果實例工程中缺失了任何需要的頭文件,它都不能被編譯。

創建一個Framework

到現在,你可能迫不及待地點著腳趾頭,想著什么時候framework可以出來。可以理解,因為到現在為止你已經做了許多工作,然而卻沒有看到過framework的身影。

現在該有所改變了,你之所以到現在都沒有創建一個framework,是因為framework本身就是靜態庫加上一組頭文件——實際上正是你已經創建好的東西。

當然,framework也有幾點不同之處:

目錄結構。Framework有一個能被Xcode識別的特殊的目錄結構,你將會創建一個build task,由它來為你創建這種結構。

片段(Slice)。目前為止,當你構建庫時,僅僅考慮到當前需要的結構(architecture)。例如,i386、arm7等,為了讓一個framework更有用,對于每一個運行framework的結構,該framework都需要構建這種結構。一會你就會創建一個新的工程,構建所有需要的結構,并將它們包含到framework中。

這一部分非常神奇,不過我們會慢慢地來。實際上它并不像看起來那樣復雜。

Framework結構

正如之前提到的,一個framework有一個特殊的目錄結構,看起來像是這樣的:


圖片.png

現在你需要在靜態庫構建過程中添加腳本來創建這種結構,在項目導航欄中選擇MYSDK,然后選擇MYSDK靜態庫目標,選擇Build Phases欄,然后選擇Editor/Add Build Phase/Add Run Script Build Phase來添加一個新的腳本。

圖片.png

這一步在build phases部分添加了一個新的面板,這允許你在構建時運行一個Bash腳本。你希望讓腳本在build的過程中何時執行,就把這個面板拖動到列表中相對應的那一位置。對于該framework工程來說,腳本最后執行,因此你可以讓它保留在默認的位置即可。


圖片.png

雙擊面板標題欄Run Script,重命名為Build Framework。

圖片.png

在腳本文本框中粘貼下面的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"

這個腳本首先創建了RWUIControls.framework/Versions/A/Headers目錄,然后創建了一個framework所需要的三個連接符號([symbolic links](https://link.jianshu.com?t=http://en.wikipedia.org/wiki/Symbolic_link))。

Versions/Current => A

Headers => Versions/Current/Headers

RWUIControls => Versions/Current/RWUIControls

最后,將公共頭文件從你之前定義的公共頭文件路徑拷貝到Versions/A/Headers目錄下,-a參數確保修飾次數作為拷貝的一部分不會改變,防止不必要的重新編譯。

現在,選擇MYSDK靜態庫scheme,然后選擇iOS Device構建目標,然后使用cmd+B構建。


圖片.png

在MYSDK工程里Products目錄下右鍵單擊MYSDK.a靜態庫,然后再一次選擇Show in Finder。

圖片.png

在這次構建目錄中你可以看到MYSDK.framework,可以確定一下這里展示了正確的目錄結構:

圖片.png

這算是在完成你的framework的過程中邁出了一大步。不過你會注意到這里并沒有一個靜態lib文件。這就是我們下一步將要解決的問題。

多架構(Multi-Architecture)編譯

iOS app需要在許多不同的CPU架構下運行:

arm7: 在最老的支持iOS7的設備上使用

arm7s: 在iPhone5和5C上使用

arm64: 運行于iPhone5S的64位 ARM 處理器 上

i386: 32位模擬器上使用

x86_64: 64為模擬器上使用

每個CPU架構都需要不同的二進制數據,當你編譯一個應用時,無論你目前正在使用那種架構,Xcode都會正確地依照對應的架構編譯。例如,如果你想跑在虛擬機上,Xcode只會編譯i386版本(或者是64位機的x86_64版本)。

這意味著編譯會盡可能快地進行,當你歸檔一款app或者構建app的發布版本(release mode)時,Xcode會構建上述三個用于真機的ARM架構。因此這樣app就可以跑在所有設備上了。不過,其他的編譯架構又如何呢?

當你創建你的framework時,你自然會想讓所有開發者都能在所有可能的架構上運行它,不是嗎?你當然想,因為這樣可以從同行那兒得到尊敬與贊美。

因此你需要讓Xcode在所有架構下都進行編譯。這一過程實際上是創建了二進制FAT(File Allocation Table,文件配置表),它包含了所有架構的片段(slice)。

Note:這里實際上強調了創建依賴靜態庫的示例項目的另一個原因:庫僅僅在示例項目運行所需要的架構下編譯,只有當有變化的時候才重新編譯,為什么這一點會讓人激動?因為開發周期會盡可能地縮短。

這里將使用在RWUIControls工程中的一個新的目標來構建framework,在項目導航欄中選擇RWUIControls,然后點擊已經存在的目標下面的Add Target按鈕。

圖片.png

找到iOS/Other/Aggregate,點擊Next,將目標命名為Framework。

Note:為什么使用集合(Aggregate)目標來創建一個framework呢?為什么這么不直接?因為OS X對庫的支持更好一些,事實上,Xcode直接為每一個OS X工程提供一個Cocoa Framework編譯目標。基于此,你將使用集合編譯目標,作為Bash腳本的連接串來創建神奇的framework目錄結構。你是不是開始覺得這里的方法有些愚蠢了?

為了確保每當這個新的framework目標被創建時,靜態鏈接庫都會被編譯,你需要往靜態庫目標中添加依賴(Dependency)。在庫工程中選擇Framework目標,在Build Phases中添加一個依賴。展開Target Dependencies面板,點擊 + 按鈕選擇MYSDK靜態庫。


圖片.png

這個目標的主要編譯部分是多平臺編譯,你將使用一個腳本來做到這一點。和你之前做的一樣,在Framework目標下,選擇Build Phases欄,點擊Editor/Add Build Phase/Add Run Script Build Phase,創建一個新的Run Script Build Phase。

圖片.png

雙擊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確保腳本的任何地方執行失敗,則整個腳本都執行失敗。這樣可以避免讓你創建一個部分有效的framework。

接著,用RW_MULTIPLATFORM_BUILD_IN_PROGRESS變量決定是否循環調用腳本,如果有該變量,則退出。

然后設定一些變量。該framework的名字與項目的名字一樣。也就是RWUIControls,另外,靜態lib的名字是libRWUIControls.a。

接下來,用腳本設置一些函數,這些函數一會項目就會用到,把下面的代碼加到腳本的底部。

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作為參數,例如iPhone7.0,然后創建靜態lib,大多數參數直接傳到當前的構建工作中來,不同的是設置ONLY_ACTIVE_ARCH來確保為當前SDK構建所有的結構。

make_fat_library使用lipo將兩個靜態庫合并為一個,其參數為兩個靜態庫和結果的輸出位置。從這里了解更多關于lipo的知識。

為了使用這兩個方法,接下來腳本將定義更多你要用到的變量,你需要知道其他SDK是什么,例如,iphoneos7.0應該對應iphonesimulator7.0,反過來也一樣。你也需要找到該SDK對應的編譯目錄。

把下面的代碼添加到腳本的底部。

# 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

上面四句聲明都非常相似,都是使用字符串比較和正則表達式來確定RW_OTHER_PLATFORM和RW_OTHER_BUILT_PRODUCTS_DIR。

詳細解釋一下上面四句聲明:

SDK_NAME將指代iphoneos7.0和iphonesimulator6.1,這個正則表達式取出字符串開頭不包含數字的那些字符,因此,其結果是iphoneos 或 iphonesimulator。

這個正則表達式取出SDK_NAME中表示版本用的數字,7.0或6.1等。

這里用簡單的字符串比較來將iphonesimulator 轉換為iphoneos,反過來也一樣。

從構建好的工程的目錄路徑的末尾找出平臺名稱,將其替換為其他平臺。這樣可以確保為其他平臺構建的目錄可以被找到。這是將兩個靜態庫合并的關鍵部分。

現在你可以啟動腳本為其他平臺編譯,然后得到合并兩靜態庫的結果。

在腳本最后添加下面的代碼:

# 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}"

首先,調用你之前定義好的函數為其他平臺編譯

如果你現在正在為模擬器編譯,那么Xcode會默認只在該系統對應的結構下編譯,例如i386 或 x86_64。為了在這兩個結構下都進行編譯,這里調用了build_static_library,基于iphonesimulator SDK重新編譯,確保這兩個結構都進行了編譯。

最后調用make_fat_library將在當前編譯目錄下的靜態lib同在其他目錄下地lib合并,依次實現支持多結構的FAT靜態庫。這個被放到了framework中。

腳本的最后是簡單的拷貝命令,將下面代碼添加到腳本最后:

# 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在所有平臺的編譯目錄中都存在。

第二條將完成的framework拷貝到用戶的桌面上,這一步是可選的,但我發現這樣做可以很方便的存取framework。

選擇Framework集合方案(aggregate scheme),按下cmd+B編譯該framework。


圖片.png

這一步將構建并在你的桌面上存放一個RWUIControls.framework。


圖片.png

為了檢查一下我們的多平臺編譯真的成功了,啟動終端,導航到桌面上的framework,像下面一樣:

$ cd ~/Desktop/MYSDK.framework

$ MYSDK.framework  xcrun lipo -info MYSDK

第一條指令導航到framework中,第二行使用lipo指令從MYSDK靜態庫中得到需要的信息,這將列出存在于該庫中的所有片段。

圖片.png

這里你可以看到,一共有五種片段:i386, x86_64, arm7, arm7s 和 arm64,正如你在編譯時設定的那樣。如果你之前使用lipo –info指令,你可以看到這些片段的一個分組。

如何使用Framework?

OK,你已經有了framework,你也有了庫。它們可以提供一種優雅的方法來解決你迄今為止還沒有遇到過的問題,但是做這些的意義是什么呢?

使用framework的其中一個主要的優點是簡化使用,現在你將創建一個簡單的iOS應用,并使用你剛剛創建好的RWUIControls.framework。

使用Xcode創建一個新工程,選擇File/New/Project,然后選擇iOS/Application/Single View Application,將新工程命名為ImageViewer,設置為僅僅用于iPhone,將其保存到與之前兩個工程同樣的目錄下。

導入framework,將SDK.framework從桌面拖到Xcode中的Frameworks組下。確保選中了Copy items into destination group’s folder。

圖片.png

打開ViewController.m,使用#import <MYSDK/MYSDK.h>導入框架的頭文件,就能使用其中的方法了.

打包(Bundle)資源

你有沒有注意到RWUIControls的framework只包含了代碼和頭文件,其他的文件卻沒有被包含。例如,你沒有使用其他任何資源,比如圖片。這是iOS的一個限制,framework只能包含頭文件和靜態庫。

現在準備好,這篇教程要開始進階了。這一部分你將學到怎么樣通過使用bundle整合資源,讓其可以隨著framework一起發布,進而突破這一限制。

你將創建一個新的UI控件——絲帶控件,作為RWUIControls庫的一部分。這個控件將在一個UIView的右上方展示一個絲帶圖片。

創建一個Bundle

資源都會被添加到bundle中,這將是RWUIControls工程上的另一個目標。

打開UIControlDevApp工程,選擇RWUIControls子工程,點擊Add Target按鈕,導航到OS X/Framework and Library/Bundle。將新的Bundle命名為RWUIControlsResources

圖片.png

這里需要配置幾個編譯設置,因為你正在創建一個在iOS上使用的bundle,這與默認的OS X不同。選擇MYSDKResources目標,然后點擊Build Settings欄,搜索base sdk,選擇Base SDK這一行,按下delete鍵,這一步將OS X切換為iOS。


圖片.png

同時你需要將工程名稱改為MYSDK。搜索product name,雙擊進入編輯模式,將${TARGET_NAME}替換為MYSDK。

圖片.png

默認情況下,有兩種resolutions的圖片可以產生一些有趣的現象。例如,當你導入一個retina @2x版本的圖片時,普通版的和Retina版的將會合并成一個多resolution的TIFF(標簽圖像文件格式,Tagged Image File Format)。這不是一件好事。搜索hidpi將COMBINE_HIDPI_IMAGES設置為NO。

圖片.png

現在,你將確保當你編譯framework時,bundle也能被編譯并將framework作為依賴添加到集體目標中。選中Framework目標,選擇Build Phases欄,展開Target Dependencies面板,點擊 + 按鈕,選擇RWUIControlsResources目標將其添加為依賴。

圖片.png

現在,在Framework目標的Build Phases中,打開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"

這條指令將拷貝構建好的bundle到用戶的桌面上。現在,編譯framework scheme,你會發現bundle在桌面上出現。

圖片.png

導入Bundle

為了用這個新的bundle開發,你需要在示例項目中使用它,這意味著你必須既把它作為依賴添加到工程中,同時作為一個對象拷貝到項目中。

在項目導航欄中,選擇UIControlDevApp工程,點擊UIControlDevApp目標,展開RWUIControls工程的Product組,把RWUIControls.bundle拖到Copy Bundle Resources面板中的 Build Phases欄。

在Target Dependencies面板中,點擊+按鈕,添加新的依賴,然后選擇MYSDKResources。

圖片.png

創建一個絲帶視圖(Ribbon View)

上面的就是所有必需的配置工作了,將你的Xib文件夾拖入到MYSDK工程下MYSDK組中。

選中Copy the items into the destination group’s folder,在對應的選擇框中打勾(1、2、4項),確保它被添加到MYSDK靜態lib目標中。

代碼中一個很重要的部分是你怎樣引用一張圖片。

UIImage *image = [UIImage imageNamed:@"MYSDK.bundle/xxx.jpg"];

Bundle就像一個文件目錄,所以引用bundle中的一張圖片是非常簡單的。

將圖片添加到bundle中,選擇這張圖片,在右邊的面板中,通過選擇來表示它應該屬于MYSDKResources目標。

結束

參考 :http://www.lxweimin.com/p/544ccde0cbd2

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容