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

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

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

一、URL Scheme

給每個(gè)App定義一個(gè)URL Scheme弹灭,通過訪問指定了URL Scheme的一個(gè)URL督暂,實(shí)現(xià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"];
    
    //先判斷是否能打開該url
    if ([[UIApplication sharedApplication] canOpenURL:url]) {
        //打開url
        [[UIApplication sharedApplication] openURL:url];
    } else {
        //給個(gè)提示或者做點(diǎn)別的事情
        NSLog(@"打不開otherApp://饥努。請(qǐng)檢查有沒有設(shè)置URL Schemes白名單或者有沒有安裝帶有otherApp://的應(yīng)用");
    }   

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

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

二管引、Share Extension

App Extension的介紹
官方給的說法是: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

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


2.png

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

名稱 說明
Bundle display name 擴(kuò)展的顯示名稱,默認(rèn)跟你的項(xiàng)目名稱相同屯援,可以通過修改此字段來控制擴(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三大類顺囊,單一肌索、混選總量不超過指定數(shù)量
NSExtensionActivationSupportsAttachmentsWithMinCount 附件最少限制,為數(shù)值類型特碳。當(dāng)設(shè)置NSExtensionActivationSupportsAttachmentsWithMaxCount時(shí)生效诚亚,默認(rèn)至少選擇1個(gè)附件,分享菜單中才顯示擴(kuò)展插件圖標(biāo)测萎。
NSExtensionActivationSupportsFileWithMaxCount 文件最多限制亡电,為數(shù)值類型。文件泛指除Image/Movie之外的附件硅瞧,例如【郵件】附件份乒、【語音備忘錄】等。<br /><br />單一腕唧、混選均不超過指定數(shù)量或辖。
NSExtensionActivationSupportsImageWithMaxCount 圖片最多限制,為數(shù)值類型枣接。單一颂暇、混選均不超過指定數(shù)量。
NSExtensionActivationSupportsMovieWithMaxCount 視頻最多限制但惶,為數(shù)值類型耳鸯。單一、混選均不超過指定數(shù)量膀曾。
NSExtensionActivationSupportsText 是否支持文本類型县爬,布爾類型,默認(rèn)不支持添谊。如【備忘錄】的分享
NSExtensionActivationSupportsWebURLWithMaxCount Web鏈接最多限制财喳,為數(shù)值類型。默認(rèn)不支持分享超鏈接斩狱,需要自己設(shè)置一個(gè)數(shù)值耳高。
NSExtensionActivationSupportsWebPageWithMaxCount Web頁面最多限制,為數(shù)值類型所踊。默認(rèn)不支持Web頁面分享泌枪,需要自己設(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;
            
        }];
    }
 
    
}

自定義分享界面

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

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

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

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

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

方法 說明
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ù)還得先來了解一下NSExtensionItem的結(jié)構(gòu)

NSExtensionItem包含四個(gè)屬性

屬性 說明
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屬性的鍵名如下:

名稱 說明
NSExtensionItemAttributedTitleKey 標(biāo)題的鍵名
NSExtensionItemAttributedContentTextKey 內(nèi)容的鍵名
NSExtensionItemAttachmentsKey 附件的鍵名
從上面的定義可以看出除了文本內(nèi)容礁扮,其他類型的內(nèi)容都是作為附件存儲(chǔ)的知举,而附件又是封裝在一個(gè)叫NSItemProvider的類型中,其定義如下:
方法 說明
initWithItem:typeIdentifier: 初始化方法太伊,item為附件的數(shù)據(jù)雇锡,typeIdentifier是附件對(duì)應(yīng)的類型標(biāo)識(shí),對(duì)應(yīng)UTI的描述。
initWithContentsOfURL: 根據(jù)制定的文件路徑來初始化僚焦。
registerItemForTypeIdentifier:loadHandler: 為一種資源類型自定義加載過程锰提。這個(gè)方法主要針對(duì)自定義資源使用,例如自己定義的類或者文件格式等芳悲。當(dāng)調(diào)用loadItemForTypeIdentifier:options:completionHandler:方法時(shí)就會(huì)觸發(fā)定義的加載過程立肘。
hasItemConformingToTypeIdentifier: 用于判斷是否有typeIdentifier(UTI)所指定的資源存在。存在則返回YES名扛,否則返回NO谅年。<br />該方法結(jié)合loadItemForTypeIdentifier:options:completionHandler:使用。
loadItemForTypeIdentifier:options:completionHandler: 加載typeIdentifier指定的資源肮韧。加載是一個(gè)異步過程融蹂,加載完成后會(huì)觸發(fā)completionHandler。
loadPreviewImageWithOptions:completionHandler: 加載資源的預(yù)覽圖片弄企。
  • 打開容器應(yīng)用的項(xiàng)目配置的Capabilities頁簽殿较,激活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è)置或訪問Group的數(shù)據(jù)放祟,不能在使用standardUserDefaults方法來獲取一個(gè)NSUserDefaults對(duì)象了鳍怨。應(yīng)該使用initWithSuiteName:方法來初始化一個(gè)NSUserDefaults對(duì)象,其中的SuiteName就是創(chuàng)建的Group的名字跪妥,然后利用這個(gè)對(duì)象來實(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:通過調(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取得共享目錄后來實(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];
}];

三、通過UTI讓我們的App支持分享

UTI(Uniform Type Identifier),一套蘋果給我們提供用來在基于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開發(fā)中,蘋果給我們提供了注冊(cè)文檔類型的接口错蝴,而這種注冊(cè)的文檔類型是全局的,系統(tǒng)中所有的應(yīng)用程序和服務(wù)都可以偵測(cè)到颓芭。因此我們通過這個(gè)底層偵測(cè)顷锰,可以使用其他可選的第三方App來預(yù)覽我們的App中不支持的文檔,而且我們還可以通過這個(gè)接口在我們的App中打開并處理第三方App的文檔亡问。

如果我們的App可以處理某些類型的實(shí)體內(nèi)容官紫,那么我們就可以在我們項(xiàng)目中的Info.plist文件中進(jìn)行注冊(cè)。當(dāng)一個(gè)第三方App通過蘋果的底層偵測(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開發(fā)時(shí)常用的屬性:

  • CFBundleTypeName("Icon File Name")
    字符串類型,指定某種類型的別名缨该,也就是用來指代我們規(guī)定的類型的別稱偎行,一般為了保持唯一性,我們使用UTI來標(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í)別來排列顯示的App的順序。優(yōu)先級(jí)別從高到低依次是Owner菱阵,Alternate,Default踢俄。None表示不接受這種類型。

程序回調(diào)
當(dāng)我們通過上面步驟晴及,成功地顯示了我的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信息共享的中心組件≈刖耄可以把它看成下一代的MIME類型歌懒。UTI是標(biāo)識(shí)資源類型(比如圖像和文本)的字符串,他們制定哪些類型的信息將用于公共數(shù)據(jù)對(duì)象溯壶,他們不需要依賴于老式的指示符及皂,比如文件擴(kuò)展名,MIME類型且改,或者文件類型的元數(shù)據(jù)
image

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

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

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

常見的MIME類型(通用型):

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

xml文檔 .xml text/xml

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

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

#import <MobileCoreServices/MobileCoreServices.h>

以下都用的是C語言編寫的

(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è)示例,說 明如何順應(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)的字典币绩。

下面例子主要是返回字典向上通過順應(yīng)性樹來構(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)系作者
  • 序言:七十年代末缆镣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子试浙,更是在濱河造成了極大的恐慌董瞻,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件川队,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡睬澡,警方通過查閱死者的電腦和手機(jī)固额,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煞聪,“玉大人斗躏,你說我怎么就攤上這事∥舾” “怎么了啄糙?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長云稚。 經(jīng)常有香客問我隧饼,道長,這世上最難降的妖魔是什么静陈? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任燕雁,我火速辦了婚禮,結(jié)果婚禮上鲸拥,老公的妹妹穿的比我還像新娘拐格。我一直安慰自己,他們只是感情好刑赶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布捏浊。 她就那樣靜靜地躺著,像睡著了一般撞叨。 火紅的嫁衣襯著肌膚如雪金踪。 梳的紋絲不亂的頭發(fā)上浊洞,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音热康,去河邊找鬼沛申。 笑死,一個(gè)胖子當(dāng)著我的面吹牛姐军,可吹牛的內(nèi)容都是我干的铁材。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奕锌,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼著觉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起惊暴,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤饼丘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后辽话,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肄鸽,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年油啤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了典徘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡益咬,死狀恐怖逮诲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幽告,我是刑警寧澤梅鹦,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站冗锁,受9級(jí)特大地震影響齐唆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜冻河,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一蝶念、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芋绸,春花似錦媒殉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春桃犬,著一層夾襖步出監(jiān)牢的瞬間刹悴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工攒暇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留土匀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓形用,卻偏偏與公主長得像就轧,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子田度,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354