iOS實(shí)現(xiàn)App之間的分享

我們?cè)趇OS平臺(tái)上想要實(shí)現(xiàn)不同App之間的內(nèi)容分享一般有幾種常用方式:

  • 給每個(gè)App定義一個(gè)URL Scheme,通過(guò)訪問(wèn)指定了URL Scheme的一個(gè)URL,實(shí)現(xiàn)直接訪問(wèn)一個(gè)APP;
  • 通過(guò)UIDocumentInteractionController或者是UIActivityViewController這倆個(gè)iOS SDK中封裝好的類在App之間發(fā)送數(shù)據(jù)、分享數(shù)據(jù)和操作數(shù)據(jù);
  • 通過(guò)App Extension,在iOS 8的SDK中提供的擴(kuò)展新特性實(shí)現(xiàn)跨App的數(shù)據(jù)操作和分享;

一、URL Scheme

給每個(gè)App定義一個(gè)URL Scheme,通過(guò)訪問(wèn)指定了URL Scheme的一個(gè)URL,實(shí)現(xiàn)直接訪問(wèn)一個(gè)APP;

//創(chuàng)建一個(gè)url,這個(gè)url就是跳轉(zhuǎn)app的url,記得加上://
    NSURL *url = [NSURL URLWithString:@"otherApp://func?key=value&key1=value1&urlschemes= selfApp"];
    
    //先判斷是否能打開(kāi)該url
    if ([[UIApplication sharedApplication] canOpenURL:url]) {
        //打開(kāi)url
        [[UIApplication sharedApplication] openURL:url];
    } else {
        //給個(gè)提示或者做點(diǎn)別的事情
        NSLog(@"打不開(kāi)otherApp://。請(qǐng)檢查有沒(méi)有設(shè)置URL Schemes白名單或者有沒(méi)有安裝帶有otherApp://的應(yīng)用");
    }   

這個(gè)方法只是創(chuàng)建了一個(gè)url,并且openURL。下面解析一下這個(gè)url的具體參數(shù),canOpenURL:方法,在iOS9環(huán)境需要設(shè)置URL Schemes白名單。

  • 使用otherApp://這個(gè)URL Scheme來(lái)打開(kāi)App
  • 使用func這個(gè)來(lái)判斷具體跳轉(zhuǎn)及相關(guān)操作等。
  • 使用key=value&key1=value1來(lái)傳參
  • 使用urlschemes= selfApp來(lái)從其他app跳轉(zhuǎn)回分享的app。

二、Share Extension

App Extension的介紹
官方給的說(shuō)法是:App Extension可以讓你擴(kuò)展你的APP的自定義功能和內(nèi)容,使用戶可以在與其他應(yīng)用或者系統(tǒng)進(jìn)行互動(dòng)的時(shí)候去使用它,
App Extension中Share Extension:分享擴(kuò)展,發(fā)布一個(gè)共享網(wǎng)站或者與其他應(yīng)用共享內(nèi)容。

1、創(chuàng)建Share Extension擴(kuò)展Target

然后選擇”iOS” -> “Application Extension” -> “Share Extension”,點(diǎn)擊“Next”。如圖


1.png

2、配置Share Extension

接下來(lái)我們需要給他一些設(shè)置。我們展開(kāi)XCode左側(cè)欄的Share目錄,找到Info.plist文件。如:


2.png

擴(kuò)展Info.plist
我們只需要關(guān)注以下幾個(gè)字段的設(shè)置

名稱 說(shuō)明
Bundle display name 擴(kuò)展的顯示名稱,默認(rèn)跟你的項(xiàng)目名稱相同,可以通過(guò)修改此字段來(lái)控制擴(kuò)展的顯示名稱。
NSExtension 擴(kuò)展描述字段,用于描述擴(kuò)展的屬性、設(shè)置等。作為一個(gè)擴(kuò)展項(xiàng)目必須要包含此字段。
NSExtensionAttributes 擴(kuò)展屬性集合字段。用于描述擴(kuò)展的屬性。
NSExtensionActivationRule 激活擴(kuò)展的規(guī)則。默認(rèn)為字符串“TRUEPREDICATE”,表示在分享菜單中一直顯示該擴(kuò)展。可以將類型改為Dictionary類型,然后添加以下字段:<br />NSExtensionActivationSupportsAttachmentsWithMaxCount<br />NSExtensionActivationSupportsAttachmentsWithMinCount<br />NSExtensionActivationSupportsImageWithMaxCount<br />NSExtensionActivationSupportsMovieWithMaxCount<br />NSExtensionActivationSupportsWebPageWithMaxCount<br />NSExtensionActivationSupportsWebURLWithMaxCount
NSExtensionMainStoryboard 設(shè)置主界面的Storyboard,如果不想使用storyboard,也可以使用NSExtensionPrincipalClass指定自定義UIViewController子類名
NSExtensionPointIdentifier 擴(kuò)展標(biāo)識(shí),在分享擴(kuò)展中為:com.apple.share-services
NSExtensionPrincipalClass 自定義UI的類名
NSExtensionActivationSupportsAttachmentsWithMaxCount 附件最多限制,為數(shù)值類型。附件包括File、Image和Movie三大類,單一、混選總量不超過(guò)指定數(shù)量
NSExtensionActivationSupportsAttachmentsWithMinCount 附件最少限制,為數(shù)值類型。當(dāng)設(shè)置NSExtensionActivationSupportsAttachmentsWithMaxCount時(shí)生效,默認(rèn)至少選擇1個(gè)附件,分享菜單中才顯示擴(kuò)展插件圖標(biāo)。
NSExtensionActivationSupportsFileWithMaxCount 文件最多限制,為數(shù)值類型。文件泛指除Image/Movie之外的附件,例如【郵件】附件、【語(yǔ)音備忘錄】等。<br /><br />單一、混選均不超過(guò)指定數(shù)量。
NSExtensionActivationSupportsImageWithMaxCount 圖片最多限制,為數(shù)值類型。單一、混選均不超過(guò)指定數(shù)量。
NSExtensionActivationSupportsMovieWithMaxCount 視頻最多限制,為數(shù)值類型。單一、混選均不超過(guò)指定數(shù)量。
NSExtensionActivationSupportsText 是否支持文本類型,布爾類型,默認(rèn)不支持。如【備忘錄】的分享
NSExtensionActivationSupportsWebURLWithMaxCount Web鏈接最多限制,為數(shù)值類型。默認(rèn)不支持分享超鏈接,需要自己設(shè)置一個(gè)數(shù)值。
NSExtensionActivationSupportsWebPageWithMaxCount Web頁(yè)面最多限制,為數(shù)值類型。默認(rèn)不支持Web頁(yè)面分享,需要自己設(shè)置一個(gè)數(shù)值。

3、處理Share Extension中的數(shù)據(jù)

其實(shí)在Share Extension中默認(rèn)都會(huì)有一個(gè)數(shù)據(jù)展現(xiàn)的UI界面ShareViewController。該界面繼承SLComposeServiceViewController這個(gè)類型
其展現(xiàn)效果,如圖:


3.png

頂部包括了標(biāo)題、取消(Cancel)按鈕和提交(Post)按鈕。然后下面跟著左邊就是一個(gè)文本編輯框,右邊就是一個(gè)圖片顯示控件。那么,每當(dāng)用戶點(diǎn)擊取消按鈕或者提交按鈕時(shí),都會(huì)分別觸發(fā)下面的方法:

/**
 *  點(diǎn)擊取消按鈕
 */
- (void)didSelectCancel
{
    [super didSelectCancel];
}

/**
 *  點(diǎn)擊提交按鈕
 */
- (void)didSelectPost
{
    // This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.

    // Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
    [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
}


- (BOOL)isContentValid {
    // Do validation of contentText and/or NSExtensionContext attachments here
    
    NSExtensionItem * imageItem = [self.extensionContext.inputItems firstObject];
    if(!imageItem)
    {
        return NO;
    }
    NSItemProvider * imageItemProvider = [[imageItem attachments] firstObject];
    if(!imageItemProvider)
    {
        return NO;
    }
    if([imageItemProvider hasItemConformingToTypeIdentifier:@"public.url"]&&self.contentText)
    {
        return YES;
    }
 
    
    return YES;
}

- (void)viewDidLoad
{
    
    NSExtensionItem * imageItem = [self.extensionContext.inputItems firstObject];
    
    NSItemProvider * imageItemProvider = [[imageItem attachments] firstObject];
    
 
    
    if([imageItemProvider hasItemConformingToTypeIdentifier:(NSString*)kUTTypeURL])
    {
        NSLog(@"xxxxxxxx");
        [imageItemProvider loadItemForTypeIdentifier:(NSString*)kUTTypeURL options:nil completionHandler:^(NSURL* imageUrl, NSError *error) {
            //在這兒做自己的工作
            NSLog(@"xxxxxxx123 = %@",imageUrl.absoluteString);
            urlString = imageUrl.absoluteString;
            
        }];
    }
 
    
}

自定義分享界面

如果通過(guò)擴(kuò)展SLComposeServiceViewController還不能滿足需求的情況下,這時(shí)候就需要自己設(shè)計(jì)一個(gè)分享視圖控制器來(lái)替換默認(rèn)的SLComposeServiceViewController

  1. 然后打開(kāi)擴(kuò)展的Info.plist文件,刪除NSExtensionMainStoryboard屬性并增加一項(xiàng)NSExtensionPrincipalClass屬性并指向CustomShareViewController(注:這里沒(méi)有使用Storyboard所以要?jiǎng)h除該屬性)

  2. 在 MainInterface.storyboard 重新拖拽一個(gè)新 UIViewController 即可,記得設(shè)置為初始 ViewController ,然后界面想怎么弄就怎么弄。

注:需要先走打開(kāi)APP 再走self.extensionContext 否則會(huì)出現(xiàn)APP無(wú)法調(diào)起來(lái)的情況

在ShareExtension中,UIViewController包含一個(gè)extensionContext這樣的上下文對(duì)象
NSExtensionContext的結(jié)構(gòu)比較簡(jiǎn)單,包含一個(gè)屬性和三個(gè)方法。其說(shuō)明如下:

方法 說(shuō)明
inputItems 該數(shù)組存儲(chǔ)著容器應(yīng)用傳入給NSExtensionContext的NSExtensionItem數(shù)組。其中每個(gè)NSExtensionItem標(biāo)識(shí)了一種類型的數(shù)據(jù)。要獲取數(shù)據(jù)就要從這個(gè)屬性入手。
completeRequestReturningItems:<br />completionHandler: 通知宿主程序的擴(kuò)展已完成請(qǐng)求。調(diào)用此方法后,擴(kuò)展UI會(huì)關(guān)閉并返回容器程序中。其中的items就是返回宿主程序的數(shù)據(jù)項(xiàng)。
cancelRequestWithError: 通知宿主程序的擴(kuò)展已取消請(qǐng)求。調(diào)用此方法后,擴(kuò)展UI會(huì)關(guān)閉并返回容器程序中。其中error為錯(cuò)誤的描述信息。
NSExtensionItemsAndErrorsKey NSExtensionItem的userInfo屬性中對(duì)應(yīng)的錯(cuò)誤信息鍵名。
從inputItems中獲取數(shù)據(jù)

inputItems是包含NSExtensionItem類型對(duì)象的數(shù)組。那么,要處理里面的數(shù)據(jù)還得先來(lái)了解一下NSExtensionItem的結(jié)構(gòu)

NSExtensionItem包含四個(gè)屬性

屬性 說(shuō)明
attributedTitle 標(biāo)題。
attributedContentText 內(nèi)容。
attachments 附件數(shù)組,包含圖片、視頻、鏈接等資源,封裝在NSItemProvider類型中。
userInfo 一個(gè)key-value結(jié)構(gòu)的數(shù)據(jù)。NSExtensionItem中的屬性都會(huì)在這個(gè)屬性中一一映射。

對(duì)應(yīng)userInfo結(jié)構(gòu)中的NSExtensionItem屬性的鍵名如下:

名稱 說(shuō)明
NSExtensionItemAttributedTitleKey 標(biāo)題的鍵名
NSExtensionItemAttributedContentTextKey 內(nèi)容的鍵名
NSExtensionItemAttachmentsKey 附件的鍵名
從上面的定義可以看出除了文本內(nèi)容,其他類型的內(nèi)容都是作為附件存儲(chǔ)的,而附件又是封裝在一個(gè)叫NSItemProvider的類型中,其定義如下:
方法 說(shuō)明
initWithItem:typeIdentifier: 初始化方法,item為附件的數(shù)據(jù),typeIdentifier是附件對(duì)應(yīng)的類型標(biāo)識(shí),對(duì)應(yīng)UTI的描述。
initWithContentsOfURL: 根據(jù)制定的文件路徑來(lái)初始化。
registerItemForTypeIdentifier:loadHandler: 為一種資源類型自定義加載過(guò)程。這個(gè)方法主要針對(duì)自定義資源使用,例如自己定義的類或者文件格式等。當(dāng)調(diào)用loadItemForTypeIdentifier:options:completionHandler:方法時(shí)就會(huì)觸發(fā)定義的加載過(guò)程。
hasItemConformingToTypeIdentifier: 用于判斷是否有typeIdentifier(UTI)所指定的資源存在。存在則返回YES,否則返回NO。<br />該方法結(jié)合loadItemForTypeIdentifier:options:completionHandler:使用。
loadItemForTypeIdentifier:options:completionHandler: 加載typeIdentifier指定的資源。加載是一個(gè)異步過(guò)程,加載完成后會(huì)觸發(fā)completionHandler。
loadPreviewImageWithOptions:completionHandler: 加載資源的預(yù)覽圖片。
  • 打開(kāi)容器應(yīng)用的項(xiàng)目配置的Capabilities頁(yè)簽,激活A(yù)pp Groups特性
  • 容器程序啟用AppGroup
    上述步驟完成后,容器程序的App Groups已經(jīng)算是設(shè)置完成。然后輪到Share Extension插件需要激活A(yù)pp Groups服務(wù),設(shè)置步驟跟容器程序相同,唯一不同的是,插件不需要?jiǎng)?chuàng)建新的App Group,只要加入到容器程序剛才創(chuàng)建的Group即可(這里可以理解為,哪些應(yīng)用要實(shí)現(xiàn)共享數(shù)據(jù),那么他們必須在同一個(gè)Group里面)。

至此,應(yīng)用和擴(kuò)展的App Groups服務(wù)都已經(jīng)啟動(dòng),現(xiàn)在就要進(jìn)行分享內(nèi)容的傳輸操作。下面分別介紹一下NSUserDefaults、NSFileManager以及CoreData三種方式是如何實(shí)現(xiàn)App Groups下的數(shù)據(jù)操作:

  • NSUserDefaults:要想設(shè)置或訪問(wèn)Group的數(shù)據(jù),不能在使用standardUserDefaults方法來(lái)獲取一個(gè)NSUserDefaults對(duì)象了。應(yīng)該使用initWithSuiteName:方法來(lái)初始化一個(gè)NSUserDefaults對(duì)象,其中的SuiteName就是創(chuàng)建的Group的名字,然后利用這個(gè)對(duì)象來(lái)實(shí)現(xiàn),跨應(yīng)用的數(shù)據(jù)讀寫,代碼如下:
//初始化一個(gè)供App Groups使用的NSUserDefaults對(duì)象
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.cn.vimfung.ShareExtensionDemo"];

//寫入數(shù)據(jù)
[userDefaults setValue:@"value" forKey:@"key"];

//讀取數(shù)據(jù)
NSLog(@"%@", [userDefaults valueForKey:@"key"]);
  • NSFileManager:通過(guò)調(diào)用 containerURLForSecurityApplicationGroupIdentifier:方法可以獲得AppGroup的共享目錄,然后在此目錄的基礎(chǔ)上實(shí)現(xiàn)任意的文件操作。代碼如下:
//獲取分組的共享目錄
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.cn.vimfung.ShareExtensionDemo"];
NSURL *fileURL = [groupURL URLByAppendingPathComponent:@"demo.txt"];

//寫入文件
[@"abc" writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:nil];

//讀取文件
NSString *str = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:nil];
NSLog(@"str = %@", str);
  • CoreData:其實(shí)CoreData是基于NSFileManager取得共享目錄后來(lái)實(shí)現(xiàn)數(shù)據(jù)共享的。即在初始化CoreData時(shí),先使用NSFileManager取得共享目錄,然后再指定共享目錄為存儲(chǔ)數(shù)據(jù)文件的目錄(如存儲(chǔ)的sqlite文件)。代碼如下:
//獲取分組的共享項(xiàng)目
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.cn.vimfung.ShareExtensionDemo"];
NSURL *storeURL = [containerURL URLByAppendingPathComponent:@"DataModel.sqlite"];

//初始化持久化存儲(chǔ)調(diào)度器
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"DataModel" withExtension:@"momd"];

NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

[coordinator addPersistentStoreWithType:NSSQLiteStoreType
                          configuration:nil
                                    URL:storeURL
                                options:nil
                                  error:nil];

//創(chuàng)建受控對(duì)象上下文
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];

[context performBlockAndWait:^{
    [context setPersistentStoreCoordinator:coordinator];
}];

三、通過(guò)UTI讓我們的App支持分享

UTI(Uniform Type Identifier),一套蘋果給我們提供用來(lái)在基于Cocoa和Cocoa Touch應(yīng)用程序中識(shí)別實(shí)體內(nèi)容類型的規(guī)范,而關(guān)于實(shí)現(xiàn)內(nèi)容關(guān)聯(lián)的技術(shù)也正是基于這套規(guī)范。在iOS和Mac OS開(kāi)發(fā)中,蘋果給我們提供了注冊(cè)文檔類型的接口,而這種注冊(cè)的文檔類型是全局的,系統(tǒng)中所有的應(yīng)用程序和服務(wù)都可以偵測(cè)到。因此我們通過(guò)這個(gè)底層偵測(cè),可以使用其他可選的第三方App來(lái)預(yù)覽我們的App中不支持的文檔,而且我們還可以通過(guò)這個(gè)接口在我們的App中打開(kāi)并處理第三方App的文檔。

如果我們的App可以處理某些類型的實(shí)體內(nèi)容,那么我們就可以在我們項(xiàng)目中的Info.plist文件中進(jìn)行注冊(cè)。當(dāng)一個(gè)第三方App通過(guò)蘋果的底層偵測(cè)技術(shù)檢查有哪些App可以處理它所指定的內(nèi)容類型時(shí),如果我們的App已經(jīng)注冊(cè)了這種類型,那么我們的App圖標(biāo)就會(huì)顯示在其中,并且作為我們自己的App的一個(gè)入口。

注冊(cè)可用類型

我們需要在info.plist文件中,添加一個(gè)新的屬性CFBundleDocumentTypes(實(shí)際上輸入的是"Document types"),這是一個(gè)數(shù)組類型的屬性,意思就是我們可以同時(shí)注冊(cè)多個(gè)類型。而針對(duì)數(shù)組中的每一個(gè)元素,都有許多屬性可以指定,詳細(xì)的屬性列表我們可以從官方文檔上找到: Core Foundation Keys ---- CFBundleDocumentTypes。這里列舉我們?cè)谧鰅OS開(kāi)發(fā)時(shí)常用的屬性:

  • CFBundleTypeName("Icon File Name")
    字符串類型,指定某種類型的別名,也就是用來(lái)指代我們規(guī)定的類型的別稱,一般為了保持唯一性,我們使用UTI來(lái)標(biāo)識(shí)。
  • CFBundleTypeIconFiles
    數(shù)組類型,包含指定的png圖標(biāo)的文件名,指定代表某種類型的圖標(biāo),而圖標(biāo)有具體的尺寸標(biāo)識(shí):
Device Sizes
iPad 64 x 64 pixels, 320 x 320 pixels
iPhone and iPod touch 22 x 29 pixels, 44 x 58 pixels (high resolution)
  • LSItemContentTypes("Document Content Type UTIs")
    數(shù)組類型,包含UTI字符串,指定我們的應(yīng)用程序所有可以識(shí)別的類型集合
  • LSHandlerRank("Handler rank")
    字符串類型,包含Owner,Default,Alternate,None四個(gè)可選值,指定對(duì)于某種類型的優(yōu)先權(quán)級(jí)別,而Launcher Service會(huì)根據(jù)這個(gè)優(yōu)先級(jí)別來(lái)排列顯示的App的順序。優(yōu)先級(jí)別從高到低依次是Owner,Alternate,DefaultNone表示不接受這種類型。

程序回調(diào)
當(dāng)我們通過(guò)上面步驟,成功地顯示了我的app圖標(biāo)之后,點(diǎn)擊圖標(biāo),我們就可以跳轉(zhuǎn)到我的應(yīng)用中,而蘋果在iOS SDK中給我們提供的接收回調(diào)的方法在iOS 9之后做出了改變,因此我們需要針對(duì)不同的設(shè)備版本做出改變:

4.png

5.png
6.png

iOS UTI (Universal Type Identifier) 通用類型標(biāo)識(shí)符

  1. 同一類型標(biāo)識(shí)符(Uniform Type Identifier,UTI)代表IOS信息共享的中心組件??梢园阉闯上乱淮腗IME類型。UTI是標(biāo)識(shí)資源類型(比如圖像和文本)的字符串,他們制定哪些類型的信息將用于公共數(shù)據(jù)對(duì)象,他們不需要依賴于老式的指示符,比如文件擴(kuò)展名,MIME類型,或者文件類型的元數(shù)據(jù)
image

如圖,顯示了Apple的基本順應(yīng)樹(shù)的一部分。這個(gè)樹(shù)上位于較低位置的任何項(xiàng)目都必須順應(yīng)其所有父數(shù)據(jù)屬性。聲明一個(gè)父UTI意味著支持他的所有子 UTI。因此,可以打開(kāi)public.data的應(yīng)用必須能打開(kāi)文本,電影,圖像文件等。其UTI的名稱類型就是public.data等

  1. MIME的了解可以去百度百科上有定義:

MIME的定義類型如下 如text/xml就是后綴.xml的MIME類型。

常見(jiàn)的MIME類型(通用型):

超文本標(biāo)記語(yǔ)言 文本 .html text/html

xml文檔 .xml text/xml

  1. 常見(jiàn)的文件擴(kuò)展名之間的相互轉(zhuǎn)換

首先要添加MobileCoreServices.framework框架,并且在頭文件中添加

#import <MobileCoreServices/MobileCoreServices.h>

以下都用的是C語(yǔ)言編寫的

(1)后綴名字符串轉(zhuǎn)化為UTI字符串

-(NSString *)preferredUTIForExtention:(NSString *)ext
{
    //Request the UTI via the file extension
    NSString *theUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)(ext), NULL);
    return theUTI;
}

(2)使用kUITagClassMIMEType作為第一個(gè)參數(shù),給UITypeCreatePreferredIdentifierForTag(),是MIME類型字符串轉(zhuǎn)化為UTI字符串

NSString *preferredUTIForMIMEType(NSString *mime)
{
    //request the UTI via the file extention
    NSString *theUTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,(__bridge CFStringRef)mime, NULL);
    return theUTI;
}

(3)使用UITypeCopyPreferredTagWithClass(),是UTI字符串轉(zhuǎn)化為后綴擴(kuò)展名

NSString *extensionForUTI(NSString *aUTI)
{
    CFStringRef theUTI = (__bridge CFStringRef)aUTI;
    CFStringRef results = UTTypeCopyPreferredTagWithClass(theUTI, kUTTagClassFilenameExtension);
    return (__bridge_transfer NSString *)results;
}

(4)UTI字符串轉(zhuǎn)化為MIME類型

NSString *mimeTypeForUTI(NSString *aUTI)
{
    CFStringRef theUTI = (__bridge CFStringRef) aUTI;
    CFStringRef results = UTTypeCopyPreferredTagWithClass(theUTI, kUTTagClassMIMEType);
    return (__bridge_transfer NSString *)results;
}

(5)測(cè)試順應(yīng)性,使用UITypeConformsTo()函數(shù)測(cè)試順應(yīng)性。該函數(shù)接受兩個(gè)參數(shù):一個(gè)源 UTI和一個(gè)要比較的UTI,如果第一個(gè)UTI順應(yīng)第二個(gè)UTI,就返回True。相等性測(cè)試則使用UITypeEqual(),下面顯示了一個(gè)示例,說(shuō) 明如何順應(yīng)性測(cè)試,確定文件路徑是否可能指向圖像資源。

BOOL pathPointsToLikelyUTIMatch(NSString *path, CFStringRef theUTI)
{
  NSString *extension = path.pathExtension;
  NSString *preferredUTI = preferredUTIForExtension(extension);
  return (UTTypeConformsTo((__bridge CFStringRef) preferredUTI, theUTI));
}
BOOL pathPointsToLikelyImage(NSString *path)
{
  return pathPointsToLikelyUTIMatch(path, CFSTR("public.image"));
}
BOOL pathPointsToLikelyAudio(NSString *path)
{
  return pathPointsToLikelyUTIMatch(path, CFSTR("public.audio"));
}

(6)獲取順應(yīng)性列表

UTTypeCopyDeclaration()是IOS API中的所有UTI函數(shù)中最一般(并且最有用)的函數(shù),它返回包含以下鍵的字典。

》kUTTypeIdentifierKey:UTI名稱,他將被傳遞給函數(shù)(例如.public.mpeg)

》kUTTypeConformsToKey:類型順應(yīng)的任何父項(xiàng)目(例如 public.mpeg順應(yīng)public.movie)

》kUTTypeDescriptionKey:正在考慮的類型(如果存在的話)的現(xiàn)實(shí)描述 (例如 “MPEG movie”)

》kUTTypeTagSpecificationKey:給定UTI的等價(jià)OSType(例如MPG和MPEG)、文件擴(kuò)展名( mpg、mpeg、mpe、m75和m15)和MIME類型(視頻/mpeg、視頻/mpg、視頻/x-mpeg和視頻/x-mpg)的字典。

下面例子主要是返回字典向上通過(guò)順應(yīng)性樹(shù)來(lái)構(gòu)建一個(gè)數(shù)組,表示給定UTI順序的所有項(xiàng)目.例如public.mpeg類型順應(yīng) public.movie public.audiovisual-content public.data public.item 和public.content,代碼如下:

NSDictionary *utiDictionary(NSString *aUTI)
{
  NSDictionary *dictionary = (__bridge_transfer NSDictionary *)UTTypeCopyDeclaration((__bridge CFStringRef) aUTI);
  return dictionary;
}
NSArray *uniqueArray(NSArray *anArray)
{
  NSMutableArray *copiedArray = [NSMutableArray arrayWithArray:anArray];
  for (id object in anArray)
  {
    [copiedArray removeObjectIdenticalTo:object];
    [copiedArray addObject:object];
  }
  return copiedArray;
}
NSArray *conformanceArray(NSString *aUTI)
{
  NSMutableArray *results = [NSMutableArray arrayWithObject:aUTI];
  NSDictionary *dictionary = utiDictionary(aUTI);
  id conforms = dictionary[(__bridge NSString *)kUTTypeConformsToKey];
  // No conformance
  if (!conforms) return results;
  // Single conformance
  if ([conforms isKindOfClass:[NSString class]])
  {
    [results addObjectsFromArray:conformanceArray(conforms)];
    return uniqueArray(results);
  }
  // Iterate through multiple conformance
  if ([conforms isKindOfClass:[NSArray class]])
  {
    for (NSString *eachUTI in (NSArray *) conforms)
      [results addObjectsFromArray:conformanceArray(eachUTI)];
    return uniqueArray(results);
  }
  // Just return the one-item array
  return results;
}
NSArray *allExtensions(NSString *aUTI)
{
  NSMutableArray *results = [NSMutableArray array];
  NSArray *conformance = conformanceArray(aUTI);
  for (NSString *eachUTI in conformance)
  {
    NSDictionary *dictionary = utiDictionary(eachUTI);
    NSDictionary *extensions = dictionary[(__bridge NSString *)kUTTypeTagSpecificationKey];
    id fileTypes = extensions[(__bridge NSString *)kUTTagClassFilenameExtension];
    if ([fileTypes isKindOfClass:[NSArray class]])
      [results addObjectsFromArray:(NSArray *) fileTypes];
    else if ([fileTypes isKindOfClass:[NSString class]])
      [results addObject:(NSString *) fileTypes];
  }
  return uniqueArray(results);
}
NSArray *allMIMETypes(NSString *aUTI)
{
  NSMutableArray *results = [NSMutableArray array];
  NSArray *conformance = conformanceArray(aUTI);
  for (NSString *eachUTI in conformance)
  {
    NSDictionary *dictionary = utiDictionary(eachUTI);
    NSDictionary *extensions = dictionary[(__bridge NSString *)kUTTypeTagSpecificationKey];
    id fileTypes = extensions[(__bridge NSString *)kUTTagClassMIMEType];
    if ([fileTypes isKindOfClass:[NSArray class]])
      [results addObjectsFromArray:(NSArray *) fileTypes];
    else if ([fileTypes isKindOfClass:[NSString class]])
      [results addObject:(NSString *) fileTypes];
  }
  return uniqueArray(results);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容