前言
朋友分享推薦給我一本PDF格式的史蒂夫?喬布斯傳
,閱讀了幾篇,很受感觸哀卫,于是想把他分享給大家欣賞閱讀。早起閑來無事撬槽,正好就接著寫篇文章來分享一下此改!我在“iOS實(shí)現(xiàn)App之間的內(nèi)容分享”這篇文章中詳細(xì)講解了通過注冊(cè)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`主要給我們提供了三種用途蓬坡,我會(huì)在下面的內(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ù),我會(huì)在后邊的文章中講解哀军。視圖中的第二行列表就是整個(gè)iOS系統(tǒng)中沉眶,可以操作PDF文檔的應(yīng)用程序列表,還包括了蘋果在`iOS 8`提供的`Share Extension`圖標(biāo)杉适,關(guān)于`Share Extension`谎倔,我會(huì)在后邊的文章中講解。視圖中的第三行列表猿推,就是現(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`框架來操作洪规,并不會(huì)對(duì)`UIDocumentInteractionController`產(chǎn)生引用丐一。當(dāng)點(diǎn)擊View上面的Button時(shí),內(nèi)部操作仍然會(huì)繼續(xù)訪問這個(gè)`UIDocumentInteractionController`實(shí)例淹冰,就會(huì)報(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)的可以手動(dòng)測試下)
展示可選操作
我們可以看到第一步圖示里面只有App圖標(biāo)叹洲,第二行操作列表中只有一個(gè)More
。所以我們來展示UIDocumentInteractionController
的第二種用途工禾,在第一步的基礎(chǔ)之上运提,顯示附加的操作選項(xiàng),闻葵。這需要我們使用UIDocumentInteractionController
提供的另外一種展示方法:
- (BOOL)presentOptionsMenuFromRect:(CGRect)rect inView:(UIView *)view animated:(BOOL)animated;
我們?cè)贐utton的觸發(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,我們可以看到下面的界面,多了Copy
和Print
的操作槽畔。Copy
操作可以將文件拷貝到系統(tǒng)粘貼板中栈妆,而Print
操作則是關(guān)聯(lián)打印機(jī)進(jìn)行打印操作的。(在這里我就不展示這倆種操作的具體界面啦!)
如果
UIDocumentInteractionController
關(guān)聯(lián)的是一個(gè)圖片文件签钩,這個(gè)界面還會(huì)提供一個(gè)Save Image
的操作,用來直接保存圖片到系統(tǒng)的Photos
中坏快,此外這個(gè)界面還提供了一個(gè)Quick Look
操作铅檩,可以讓我們直接預(yù)覽喬布斯自傳
PDF文檔,只不過需要我們?cè)俣鄬扅c(diǎn)代碼莽鸿,為了文章的合理性和結(jié)構(gòu)性昧旨,我決定在下面的標(biāo)題內(nèi)容中講解。(先賣個(gè)小關(guān)子O榈谩兔沃!)
直接預(yù)覽
UIDocumentInteractionController
第三種預(yù)覽文檔內(nèi)容的用途非常重要,而且也是常見的级及。我會(huì)詳細(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
的代理對(duì)象,并且實(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)容议经,所以我們?cè)贐utton的觸發(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ù)覽操作
通過上面的操作我們就可以欣賞閱讀我們想看的史蒂夫?喬布斯傳
啦嗓袱,不過別忘記我們上面還賣了一個(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é)中都做完了缺菌,比如指定代理對(duì)象葫辐,然后實(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)顯示出來啦!如下圖:
如果我們點(diǎn)擊這個(gè)Quick Look
操作狐胎,就可以看到直接預(yù)覽內(nèi)容時(shí)所展示的界面啦鸭栖。好啦,通過UIDocumentInteractionController
實(shí)現(xiàn)史蒂夫?喬布斯傳
的預(yù)覽和分享就到此結(jié)束啦握巢。我會(huì)在下面的章節(jié)中晕鹊,講解通過其他技術(shù)實(shí)現(xiàn)喬布斯自傳
的分享和操作。