通過UIDocumentInteractionController預(yù)覽和分享"史蒂夫?喬布斯傳"

前言

朋友分享推薦給我一本PDF格式的史蒂夫?喬布斯傳,閱讀了幾篇,很受感觸,于是想把他分享給大家欣賞閱讀。早起閑來無事,正好就接著寫篇文章來分享一下!我在“iOS實(shí)現(xiàn)App之間的內(nèi)容分享”這篇文章中詳細(xì)講解了通過注冊UTI的方式讓我們的App支持分享,也簡單地說了一下App內(nèi)部怎么處理分享。同時(shí),我也指出了在iOS系統(tǒng)跨App分享內(nèi)容的幾種常用技術(shù),比如URL Scheme,AirDrop, UIDocumentInteractionController,UIActivityViewController這幾種。這一篇文章,我們來談一下最基礎(chǔ)的原始方法,怎么通過使用UIDocumentInteractionController來預(yù)覽、操作和分享史蒂夫?喬布斯傳

簡介

從iOS SDK的API文檔中,我們可以找到UIDocumentInteractionController的聲明:

NS_CLASS_AVAILABLE_IOS(3_2) __TVOS_PROHIBITED @interface UIDocumentInteractionController : NSObject <UIActionSheetDelegate>


由此聲明我們可以得知,`UIDocumentInteractionController`是從iOS 3.2的SDK開始支持的,它是直接繼承的`NSObject`,而不是我們想象的`UIViewController`,因此我們需要使用`UIDocumentInteractionController`提供的方法來展示它,而且我們還可以看出它是不能在Apple TV 的開發(fā)中使用的。遍觀`UIDocumentInteractionController`的屬性和方法可以看出,`UIDocumentInteractionController`主要給我們提供了三種用途,我會在下面的內(nèi)容中逐條的講解`UIDocumentInteraction`的每一種用途的具體使用:
>1. 展示一個(gè)可以操作我們分享的文檔類型的第三方App列表
2. 在第一條展示列表的基礎(chǔ)上添加額外的操作,比如`復(fù)制`,`打印`,`預(yù)覽`,`保存`等。
3. 結(jié)合`Quick Look`框架直接展示文檔內(nèi)容

#準(zhǔn)備階段
首先我創(chuàng)建了一個(gè)新的應(yīng)用方便演示和截圖,我把它命名為`ZSDocumentInteractionTest`,然后拖入PDF格式的`史蒂夫?喬布斯傳`到`ZSDocumentInteractionTest`項(xiàng)目的bundle中。然后在`Storyboard`的`ViewController`中添加了一個(gè)Button作為`UIDocumentInteractionController`的觸發(fā)操作(這些操作都比較簡單,就不在這里用圖展示啦)。運(yùn)行程序,我們就可以看到Button啦,截圖如下。然后我們就可以在Button的觸發(fā)方法中,操作`UIDocumentInteractionController`來顯示或者分享我們的`史蒂夫?喬布斯傳`啦,具體的應(yīng)用詳情可以參考GitHub上的Demo:[ZSDocumentInteractionTest](https://github.com/SeraZheng/ZSDocumentInteractionTest)。

![在ViewController的視圖 中顯示Button](http://upload-images.jianshu.io/upload_images/1216322-9fac2efc2d9a6bc4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#初始化
不管我們使用哪種`UIDocumentInteractionController`的展示方式和用途,都需要給`UIDocumentInteractionController`指定文檔的URL,所以我們通常使用下面的初始化方式,給`UIDocumentInteractionController`指定`文件的URL`。
  • (IBAction)presentPDFDocumentInteraction:(id)sender {
    UIDocumentInteractionController *documentController = [UIDocumentInteractionController interactionControllerWithURL:[[NSBundle mainBundle] URLForResource:@"Steve" withExtension:@"pdf"]];
    }

#展示第三方App列表
我們先實(shí)現(xiàn)`UIDocumentInteractionController`的第一個(gè)用途,展示可以操作PDF文件的第三方App列表。我們需要使用`UIDocumentInteractionController`提供的方法:
  • (BOOL)presentOpenInMenuFromRect:(CGRect)rect inView:(UIView *)view animated:(BOOL)animated;

我在Button的觸發(fā)方法中添加下面的代碼,意思就是讓`UIDocumentInteractionController`的View在當(dāng)前控制器視圖上顯示:
[documentController presentOpenInMenuFromRect:self.view.bounds inView:self.view animated:YES];
運(yùn)行程序,點(diǎn)擊Button,我們可以開始第一次展示測試?yán)病?
#第一次展示測試
一切準(zhǔn)備就緒之后,我開始進(jìn)行`UIDocumentInteractionController`的測試,點(diǎn)擊Button,就可以看到下面的界面啦。這說明我們的第一步成功了!!(真棒)

![可以操作PDF文件的第三方App列表](http://upload-images.jianshu.io/upload_images/1216322-f1a0a8924ea4b42d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

簡單介紹一下這個(gè)界面,這個(gè)視圖中的第一行列表顯示`AirDrop`,是蘋果在`iOS 7`提供的一種跨設(shè)備分享的技術(shù),我會在后邊的文章中講解。視圖中的第二行列表就是整個(gè)iOS系統(tǒng)中,可以操作PDF文檔的應(yīng)用程序列表,還包括了蘋果在`iOS 8`提供的`Share Extension`圖標(biāo),關(guān)于`Share Extension`,我會在后邊的文章中講解。視圖中的第三行列表,就是現(xiàn)實(shí)設(shè)備可選的操作,如`Copy`,`Print`中,這里什么操作都沒有,并不是說沒有可執(zhí)行的操作,而是我們沒有讓他顯示出來。

接著我試著點(diǎn)擊QQ圖標(biāo),打算把`史蒂夫?喬布斯傳`分享給我的好友,然而意外發(fā)生了,`ZSDocumentInteractionTest`崩潰掉啦,而且還給出我們一段錯(cuò)誤提示:

2015-12-30 19:00:40.078 ZSDocumentInteractionTest[1254:344240] *** Assertion failure in -[_UIOpenWithAppActivity performActivity], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3512.29.5/UIDocumentInteractionController.m:408
2015-12-30 19:00:40.079 ZSDocumentInteractionTest[1254:344240] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UIDocumentInteractionController has gone away prematurely!'
*** First throw call stack:
(0x248e185b 0x35fa2dff 0x248e1731 0x25672ddb 0x290638c9 0x292695bb 0x28d5aefd 0x28d5e1a1 0x28b42107 0x28a50a55 0x28a50531 0x28a5042b 0x282e05cf 0x1acd03 0x1b17c9 0x248a4535 0x248a2a2f 0x247f50d9 0x247f4ecd 0x2db6aaf9 0x28a7e2dd 0x780ad 0x366f0873)
libc++abi.dylib: terminating with uncaught exception of type NSException

我看到錯(cuò)誤提示竟然指向了`UIDocumentInteractionController.m`文件,而且錯(cuò)誤提示是`NSInternalInconsistencyException`(內(nèi)部不一致)和"UIDocumentInteractionController has gone away prematurely!"(UIDocumentInteractionController過早地被釋放掉啦)。由此我想出這個(gè)應(yīng)該是內(nèi)存過早釋放的一個(gè)錯(cuò)誤,然后我查閱了一下Apple Developer上的文檔,原來,在ARC環(huán)境下展示`UIDocumentInteractionController`時(shí),當(dāng)我的函數(shù)方法調(diào)用完畢,退棧之后,`UIDocumentInteractionController`的實(shí)例就被釋放掉了,展示出來的這個(gè)View由`Quick Look`框架來操作,并不會對`UIDocumentInteractionController`產(chǎn)生引用。當(dāng)點(diǎn)擊View上面的Button時(shí),內(nèi)部操作仍然會繼續(xù)訪問這個(gè)`UIDocumentInteractionController`實(shí)例,就會報(bào)出上述錯(cuò)誤。

錯(cuò)誤原因找到了,那么解決原理也就清楚了,只要不讓`UIDocumentInteractionController`實(shí)例過早釋放就可以啦。我們可以將`UIDocumentInteractionController`聲明為一個(gè)`strong`類型的實(shí)例屬性,然后修改一下Button觸發(fā)方法就可以啦。(仍然不理解的朋友可以去GitHub上下載Demo測試)

@interface ViewController ()
@property (nonatomic, strong) UIDocumentInteractionController *documentController;
@end

我在Button的觸發(fā)方法中添加下面方法的調(diào)用,為了方便區(qū)分和理解,我把代碼封裝成了私有實(shí)例方法:
  • (void)presentOpenInMenu
    {
    // display third-party apps
    [self.documentController presentOpenInMenuFromRect:self.view.bounds inView:self.view animated:YES];
    }

```Objective-c
- (IBAction)presentPGNDocumentInteraction:(id)sender {
    _documentController = [UIDocumentInteractionController interactionControllerWithURL:[[NSBundle mainBundle] URLForResource:@"Steve" withExtension:@"pdf"]];
    [self presentOpenInMenu];
}

修改完之后,運(yùn)行程序,然后點(diǎn)擊Button,看到第一次測試時(shí)展示出來的圖片啦。然后再點(diǎn)QQ圖標(biāo),就可以正確地跳轉(zhuǎn)到QQ程序中,選擇好友就可以分享史蒂夫?喬布斯傳啦。(QQ接收分享頁面就不展示了,想試驗(yàn)的可以手動測試下)

展示可選操作

我們可以看到第一步圖示里面只有App圖標(biāo),第二行操作列表中只有一個(gè)More。所以我們來展示UIDocumentInteractionController的第二種用途,在第一步的基礎(chǔ)之上,顯示附加的操作選項(xiàng),。這需要我們使用UIDocumentInteractionController提供的另外一種展示方法:

- (BOOL)presentOptionsMenuFromRect:(CGRect)rect inView:(UIView *)view animated:(BOOL)animated;

我們在Button的觸發(fā)方法中添加下面方法的調(diào)用:

- (void)presentOptionsMenu
{
    // display third-party apps as well as actions, such as Copy, Print, Save Image, Quick Look
    [_documentController presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES];
}

運(yùn)行程序,點(diǎn)擊Button,我們可以看到下面的界面,多了CopyPrint的操作。Copy操作可以將文件拷貝到系統(tǒng)粘貼板中,而Print操作則是關(guān)聯(lián)打印機(jī)進(jìn)行打印操作的。(在這里我就不展示這倆種操作的具體界面啦?。?/p>

第三方App列表和附加操作界面

如果UIDocumentInteractionController關(guān)聯(lián)的是一個(gè)圖片文件,這個(gè)界面還會提供一個(gè)Save Image的操作,用來直接保存圖片到系統(tǒng)的Photos中,此外這個(gè)界面還提供了一個(gè)Quick Look操作,可以讓我們直接預(yù)覽喬布斯自傳PDF文檔,只不過需要我們再多寫點(diǎn)代碼,為了文章的合理性和結(jié)構(gòu)性,我決定在下面的標(biāo)題內(nèi)容中講解。(先賣個(gè)小關(guān)子?。?

直接預(yù)覽

UIDocumentInteractionController第三種預(yù)覽文檔內(nèi)容的用途非常重要,而且也是常見的。我會詳細(xì)地說一下如何通過UIDocumentInteractionController實(shí)現(xiàn)預(yù)覽史蒂夫?喬布斯傳。首先你需要為UIDocumentInteractionController指定一個(gè)delegate,并且實(shí)現(xiàn)下面的代理方法:

- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller;

這個(gè)代理方法主要是用來指定UIDocumentInteractionController要顯示的視圖所在的父視圖容器。這樣UIDocumentInteractionController才清楚在哪里展示Quick Look預(yù)覽內(nèi)容, 我在這里就指定Button所在的UIViewController來做UIDocumentInteractionController的代理對象,并且實(shí)現(xiàn)上面的代理方法。在Button的觸發(fā)方法中添加下面的代碼

_documentController.delegate = self;

然后實(shí)現(xiàn)代理方法:

- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller
{
    return self;
}

UIDocumentInteractionController是繼承自NSObject的,因而為了能夠?qū)崿F(xiàn)直接預(yù)覽,我們需要用到UIDocumentInteractionController提供的展示預(yù)覽的方法,

- (BOOL)presentPreviewAnimated:(BOOL)animated;

這個(gè)方法是以模態(tài)窗口通過Quick Look框架全屏顯示PDF的內(nèi)容,所以我們在Button的觸發(fā)方法中添加下面方法的調(diào)用:

- (void)presentPreview
{
    // display PDF contents by Quick Look framework
    [self.documentController presentPreviewAnimated:YES];
}

然后運(yùn)行程序,點(diǎn)擊Button,彈出了一個(gè)新視圖,可以看到史蒂夫?喬布斯傳的內(nèi)容,如下圖

直接預(yù)覽喬布斯自傳

展示預(yù)覽操作

通過上面的操作我們就可以欣賞閱讀我們想看的史蒂夫?喬布斯傳啦,不過別忘記我們上面還賣了一個(gè)小關(guān)子,就是在展示可選操的時(shí)候,除了Copy,Print,其實(shí)我們還可以展示Quick Look這個(gè)預(yù)覽操作。為什么我要賣關(guān)子呢,因?yàn)槲沂且粋€(gè)相信因果循環(huán)的人,我組織文章的邏輯是由淺入深,我設(shè)想通過一步步鋪墊來展開UIDocumentInteractionController所有特性。

好啦,回歸正題!我們想要實(shí)現(xiàn)顯示Quick Look預(yù)覽操作,其大部分的工作在直接預(yù)覽這一小節(jié)中都做完了,比如指定代理對象,然后實(shí)現(xiàn)這個(gè)代理方法來指定UIDocumentInteractionController的父視圖容器:

- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller;

由于我們已經(jīng)做完了所有準(zhǔn)備,在這一步,我們只需要將直接展示史蒂夫?喬布斯傳內(nèi)容的方法替換為下面這段,展示可選操作列表的方法,就可以啦!

- (void)presentOptionsMenu
{
    // display third-party apps as well as actions, such as Copy, Print, Save Image, Quick Look
    [_documentController presentOptionsMenuFromRect:self.view.bounds inView:self.view animated:YES];
}

然后我們運(yùn)行程序,點(diǎn)擊Button,就可以看到Quick Look操作已經(jīng)顯示出來啦!如下圖:

展示Quick Look操作

如果我們點(diǎn)擊這個(gè)Quick Look操作,就可以看到直接預(yù)覽內(nèi)容時(shí)所展示的界面啦。好啦,通過UIDocumentInteractionController實(shí)現(xiàn)史蒂夫?喬布斯傳的預(yù)覽和分享就到此結(jié)束啦。我會在下面的章節(jié)中,講解通過其他技術(shù)實(shí)現(xiàn)喬布斯自傳的分享和操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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