通過UIActivityViewController實現更多分享服務

前言

我在通過UIDocumentInteractionController預覽和分享"史蒂夫?喬布斯傳"這篇文章中鼓择,詳細講了UIDocumentInteractionController的用途和使用方法。而在iOS 6 SDK中,蘋果提供了UIActivityViewController來讓我們可以使用更多地服務碍侦。這篇文章,我就來介紹一下怎么通過UIActivityViewController實現更多地服務隶糕。

簡介

打開UIActivityViewController的API文檔瓷产,我們可以看到UIActivityViewController的聲明。

NS_CLASS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED @interface UIActivityViewController : UIViewController

我們可以看出UIActivityViewController是在iOS 6開始支持的枚驻,同樣是不能在Apple TV的開發(fā)中使用濒旦。而且UIActivityViewController是直接繼承UIViewController的,這意味著我們需要自己來展示和解散視圖再登。

準備

我使用在UIDocumentInteractionController測試中使用的Demo,GitHub地址是:ZSDocumentInteractionTest尔邓。然后添加一個新的Button作為UIActivityViewController的觸發(fā)事件,運行程序霎冯,就可以看到下面的界面啦(過程自行想象铃拇,哈哈)

Screen Shot 2015-12-31 at 15.17.52.png

初始化

接著我們在Button的觸發(fā)方法里面開始操作UIActivityViewController來提供服務。首先沈撞,我們需要初始化一個UIActivityViewController的實例慷荔,UIActivityViewController提供了一個初始化方法:

- (instancetype)initWithActivityItems:(NSArray *)activityItems applicationActivities:(nullable NSArray<__kindof UIActivity *> *)applicationActivities NS_DESIGNATED_INITIALIZER;

官方文檔對這倆個參數有詳細的解釋:

參數 描述
activityItems The array of data objects on which to perform the activity. The type of objects in the array is variable and dependent on the data your application manages. For example, the data might consist of one or more string or image objects representing the currently selected content. Instead of actual data objects, the objects in this array can be objects that adopt the UIActivityItemSource protocol, such as UIActivityItemProvider objects. Source and provider objects act as proxies for the corresponding data in situations where you do not want to provide that data until it is needed. Note that you should not resuse an activity view controller object that includes a UIActivityItemProvider object in its activityItems array.This array must not be nil and must contain at least one object.
applicationActivities An array of UIActivity objects representing the custom services that your application supports. This parameter may be nil.

大概意思是這個方法接收倆個數組類型的參數,第一個數組內的對象代表的是我們想要操作的數據的一些表征,而且這個數組至少需要一個值显晶,比如我們PDF文檔的名稱贷岸,URL;第二個數組指定了泛型磷雇,數組內的對象必須是UIActivity類型的對象偿警,代表的是iOS系統(tǒng)支持的我們自定義的服務,關于這點我在后面自定義UIActivity服務的內容中會講解唯笙,現在我們暫時置為nil螟蒸。代碼如下:

- (IBAction)presentPDFActivityView:(id)sender {
    UIActivityViewController *activity = [[UIActivityViewController alloc] initWithActivityItems:@[@"Steve Jobs by waiter lsaacson", [[NSBundle mainBundle] URLForResource:@"Steve" withExtension:@"pdf"]] applicationActivities:nil];

視圖展示

UIActivityViewController是直接繼承UIViewController的,看到這崩掘,你想象可以通過自己的需求來使用不同的方式展示UIActivityViewController啦七嫌,然而事與愿違。

官方文檔中是這么說的: “When presenting the view controller, you must do so using the appropriate means for the current device. On iPad, you must present the view controller in a popover. On iPhone and iPod touch, you must present it modally”苞慢。 大概意思是說诵原,展示UIActivityViewController的時候需要根據當前的設備類型選擇合適的展示方式,在iPad設備上就必須在'popover'視圖里面展示挽放,在其他設備上绍赛,必須以模態(tài)視圖展示。

個人認為開發(fā)必須持懷疑和驗證的態(tài)度辑畦,所以我嘗試在一個UINavigationController中push一個UIActivityController,代碼如下:

- (IBAction)presentPDFActivityView:(id)sender {
    
    UIActivityViewController *activity = [[UIActivityViewController alloc] initWithActivityItems:@[@"Steve Jobs by waiter lsaacson", [[NSBundle mainBundle] URLForResource:@"Steve" withExtension:@"pdf"]] applicationActivities:nil];
    [self.navigationController pushViewController:activity animated:YES];
}

然后運行程序吗蚌,點擊Button,意料之中程序崩潰掉了纯出,給出我們的錯誤反饋是:

2015-12-31 15:03:03.733 ZSDocumentInteractionTest[9307:971136] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'UIActivityViewController can only be used modally or as contentViewController in popover on iPad.'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000103197e65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x0000000102c10deb objc_exception_throw + 48
    2   CoreFoundation                      0x0000000103197d9d +[NSException raise:format:] + 205
    3   UIKit                               0x0000000103e68e55 -[UIActivityViewController viewDidAppear:] + 533
    4   UIKit                               0x00000001036e0949 -[UIViewController _setViewAppearState:isAnimating:] + 830
    5   UIKit                               0x00000001036e12cc -[UIViewController _endAppearanceTransition:] + 262
    6   UIKit                               0x000000010371bf63 -[UINavigationController navigationTransitionView:didEndTransition:fromView:toView:] + 1290
    7   UIKit                               0x0000000103711d24 __49-[UINavigationController _startCustomTransition:]_block_invoke + 233
    8   UIKit                               0x0000000103f4ad20 -[_UIViewControllerTransitionContext completeTransition:] + 101
    9   UIKit                               0x000000010352cfff __53-[_UINavigationParallaxTransition animateTransition:]_block_invoke95 + 834
    10  UIKit                               0x00000001035f1076 -[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 644
    11  UIKit                               0x00000001035ce2af -[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 241
    12  UIKit                               0x00000001035ce65e -[UIViewAnimationState animationDidStop:finished:] + 80
    13  QuartzCore                          0x00000001070c2fa0 _ZN2CA5Layer23run_animation_callbacksEPv + 308
    14  libdispatch.dylib                   0x000000010589f49b _dispatch_client_callout + 8
    15  libdispatch.dylib                   0x00000001058872af _dispatch_main_queue_callback_4CF + 1738
    16  CoreFoundation                      0x00000001030f7d09 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    17  CoreFoundation                      0x00000001030b92c9 __CFRunLoopRun + 2073
    18  CoreFoundation                      0x00000001030b8828 CFRunLoopRunSpecific + 488
    19  GraphicsServices                    0x0000000106954ad2 GSEventRunModal + 161
    20  UIKit                               0x0000000103544610 UIApplicationMain + 171
    21  ZSDocumentInteractionTest           0x000000010270b6af main + 111
    22  libdyld.dylib                       0x00000001058d392d start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

我們看出錯誤說明是"UIActivityViewController can only be used modally or as contentViewController in popover on iPad."因此我們需要更換一下展示方法褪测,在手機上已一個模態(tài)視圖的方式展示,而在iPad上則作為popover的內容視圖展示潦刃。代碼如下:

- (IBAction)presentPDFActivityView:(id)sender {
    
    UIActivityViewController *activity = [[UIActivityViewController alloc] initWithActivityItems:@[[[NSBundle mainBundle] URLForResource:@"Steve" withExtension:@"pdf"]] applicationActivities:@[[[ZSCustomActivity alloc] init]]];
    activity.excludedActivityTypes = @[UIActivityTypeAirDrop];
    
    // incorrect usage
    // [self.navigationController pushViewController:activity animated:YES];
    
    UIPopoverPresentationController *popover = activity.popoverPresentationController;
    if (popover) {
        popover.sourceView = self.activityButton;
        popover.permittedArrowDirections = UIPopoverArrowDirectionUp;
    }
    
    [self presentViewController:activity animated:YES completion:NULL];
}

再次運行代碼,點擊Button懈叹,就可以看到下面的界面啦(完美展示)

展示UIActivityViewController

然后我們就可以選擇服務來操作和分享史蒂夫?喬布斯傳啦乖杠。

excludedActivityTypes

UIActivityViewController相比于UIDocumentInteractionController優(yōu)勢除了可以添加額外的自定義服務,它還提供了非常好的原生服務的定制化功能澄成。我們可以完全根據自己的需求胧洒,控制UIActivityViewController提供的系統(tǒng)服務的顯示,比如我不想展示AirDrop這個功能墨状,而這點在UIDocumentInteractionController是做不到的城舞。想做到這一點靶端,就需要使用到UIActivityViewController提供的一個屬性:

@property(nullable, nonatomic, copy) NSArray<NSString *> *excludedActivityTypes; // default is nil. activity types listed will not be displayed

正如注釋中提到的,excludedActivityTypes這個屬性包含了所有不想在UIActivityViewController中展示的Item服務。excludedActivityTypes是一個字符串數組随闺,所包含的內容必須是系統(tǒng)提供的UIActivityactivityType字符串,而系統(tǒng)提供的字符串如下:

NSString *const UIActivityTypePostToFacebook;
NSString *const UIActivityTypePostToTwitter;
NSString *const UIActivityTypePostToWeibo;
NSString *const UIActivityTypeMessage;
NSString *const UIActivityTypeMail;
NSString *const UIActivityTypePrint;
NSString *const UIActivityTypeCopyToPasteboard;
NSString *const UIActivityTypeAssignToContact;
NSString *const UIActivityTypeSaveToCameraRoll;
NSString *const UIActivityTypeAddToReadingList;
NSString *const UIActivityTypePostToFlickr;
NSString *const UIActivityTypePostToVimeo;
NSString *const UIActivityTypePostToTencentWeibo;
NSString *const UIActivityTypeAirDrop;

如果我們不想展示AirDrop功能,我們把UIActivityTypeAirDrop添加到excludedActivityTypes里面:

activity.excludedActivityTypes = @[UIActivityTypeAirDrop];

運行程序,點擊Button饼煞,我們可以看到下面的界面發(fā)生的變化。

隱藏AirDrop功能

自定義UIActivity服務

UIActivityViewController相比于UIDocumentInteractionController的最大優(yōu)勢就是UIActivityViewController所提供的自定義服務诗越,我們可以通過UIActivityUIActivityViewController上添加我們自定義的服務砖瞧。

官方文檔上對UIActivity有一段解釋,"This class must be subclassed before it can be used. The job of an activity object is to act on the data provided to it and to provide some meta information that iOS can display to the user. For more complex services, an activity object can also display a custom user interface and use it to gather additional information from the user."嚷狞。其大概意思是块促,UIActivity必須通過繼承來使用,它主要是操作給用戶展示的信息床未,而且還可以操作展示定制化的界面來獲取更多地數據信息竭翠。

現在我們打算自定義一個叫ZS Custom的服務,所以我們創(chuàng)建一個ZSCustomActivity得類來繼承UIActivity即硼,除此之外逃片,我們必須重寫下面的幾個方法:

  • activityType

    - (nullable NSString *)activityType;       // default returns nil. subclass may override to return custom activity type that is reported to completion handler
    

    這是用來標識自定義服務的一個字符串,而系統(tǒng)提供的服務的標識在上面我們已經提到了;為了迎合iOS SDK中的規(guī)范只酥,我給它返回一個UIActivityTypeZSCustomMine褥实,定義如下:

    NSString *const UIActivityTypeZSCustomMine = @"ZSCustomActivityMine";
    
    - (NSString *)activityType
    {
        return UIActivityTypeZSCustomMine;
    }
    
  • activityTitle

    - (nullable NSString *)activityTitle;      // default returns nil. subclass must override and must return non-nil value
    

    UIActivityViewController中給用戶展示的服務的名稱,比如上面圖片中的"Copy","Print",我們自定義的服務名稱為ZS Custom

    - (NSString *)activityTitle
    {
        //國際化
        return NSLocalizedString(@"ZS Custom", @"");
    }
    
  • activityImage

    - (nullable UIImage *)activityImage;       // default returns nil. subclass must override and must return non-nil value
    

    UIActivityViewController中給用戶展示的服務的圖標裂允。關于這里的圖標损离,有非常嚴格的限制:

    • 首先是圖標的背景色,這里推薦最好的完全透明的背景色绝编。

    官方文檔中是這么解釋的僻澎,"The alpha channel of the image is used as a mask to generate the final image that is presented to the user. Any color data in the image itself is ignored. Opaque pixels have a gradient applied to them and this gradient is then laid on top of a standard background. Thus, a completely opaque image would yield a gradient filled rectangle",意思大概是,在這里顏色數據會被忽略十饥,而透明圖層會被當做mask(蒙版圖層)窟勃,不透明的圖片會顯示成漸進色填充。

    • 其次是圖標的尺寸逗堵,在不同的設備需要不同的尺寸秉氧,因此需要準備一套圖標。
Device iOS Version Icon Size(pt)
iPhone蜒秤、iPod Touch iOS 6 < 43x43
iPhone汁咏、iPod Touch iOS 7+ 60x60
iPad iOS 6 < 60x60
iPad iOS 7+ 76x76
Retina All @2x
  • canPerformWithActivityItems:

    - (BOOL)canPerformWithActivityItems:(NSArray *)activityItems;   // override this to return availability of activity based on items. default returns NO
    
    

    指定可以處理的數據類型,如果可以處理則返回YES

  • prepareWithActivityItems:

    - (void)prepareWithActivityItems:(NSArray *)activityItems;      // override to extract items and set up your HI. default does nothing
    

    在用戶選擇展示在UIActivityViewController中的自定義服務的圖標之后作媚,調用自定義服務處理方法之前的準備工作攘滩,都需要在這個方法中指定,比如可以根據數據展示一個界面來獲取用戶指定的額外數據信息

  • activityCategory

    + (UIActivityCategory)activityCategory NS_AVAILABLE_IOS(7_0); //       default is UIActivityCategoryAction.
    

    UIActivityViewController中的服務分為了倆種纸泡,UIActivityCategoryActionUIActivityCategoryShare,``UIActivityCategoryAction表示在最下面一欄的操作型服務,比如Copy漂问、Print;UIActivityCategoryShare表示在中間一欄的分享型服務冒黑,比如一些社交軟件。

  • performActivity

    - (void)performActivity; // if no view controller, this method is called. call activityDidFinish when done. default calls [self activityDidFinish:NO]
    

    在用戶選擇展示在UIActivityViewController中的自定義服務的圖標之后勤哗,而且也調用了prepareWithActivityItems:,就會調用這個方法執(zhí)行具體的服務操作

需要的方法都重寫好之后抡爹,運行程序,點擊Button芒划,就可以看到我們自定義的服務圖標顯示在了UIActivityViewController中冬竟。

自定義服務ZS Custom

補充之AirDrop

前面一直提到AirDrop,我們在這里額外補充一下AirDrop的相關知識點。AirDrop是在iOS 7中提供的民逼,實現跨設備傳輸文檔的功能拼苍。AirDrop的實現基于藍牙創(chuàng)建一種類似WIFI的”點對點網絡“笑诅,然后實現跨設備傳輸功能。

只是AirDrop的傳輸是有限制的疮鲫,我們可以在我們的App中通過AirDrop傳送內容吆你,卻不能實現通過AirDrop接收內容,因為燕侠,蘋果把設備上通過AirDrop接收到的內容都放到了自家App上者祖,比如僅僅傳送文字時,在接收設備上就會通過Notes打開绢彤;如果傳送圖片七问,在接收設備上就會保存到Photos應用中;通過URL傳送文件茫舶,在接收設備上就會通過Safari打開烂瘫。

只要是有UIDocumentInteractionControllerUIActivityViewController展示的地方,都可以展示AirDrop功能奇适。關于AirDrop如何連接設備,如何傳送芦鳍,可以到百度經驗找完美得教程嚷往。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市柠衅,隨后出現的幾起案子皮仁,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贷祈,死亡現場離奇詭異趋急,居然都是意外死亡,警方通過查閱死者的電腦和手機势誊,發(fā)現死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門呜达,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人粟耻,你說我怎么就攤上這事查近。” “怎么了挤忙?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵霜威,是天一觀的道長。 經常有香客問我册烈,道長戈泼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任赏僧,我火速辦了婚禮大猛,結果婚禮上,老公的妹妹穿的比我還像新娘次哈。我一直安慰自己胎署,他們只是感情好,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布窑滞。 她就那樣靜靜地躺著琼牧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哀卫。 梳的紋絲不亂的頭發(fā)上巨坊,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音此改,去河邊找鬼趾撵。 笑死,一個胖子當著我的面吹牛共啃,可吹牛的內容都是我干的占调。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼移剪,長吁一口氣:“原來是場噩夢啊……” “哼究珊!你這毒婦竟也來了?” 一聲冷哼從身側響起纵苛,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤剿涮,失蹤者是張志新(化名)和其女友劉穎言津,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體取试,經...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡悬槽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了瞬浓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片初婆。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瑟蜈,靈堂內的尸體忽然破棺而出烟逊,到底是詐尸還是另有隱情,我是刑警寧澤铺根,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布宪躯,位于F島的核電站,受9級特大地震影響位迂,放射性物質發(fā)生泄漏访雪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一掂林、第九天 我趴在偏房一處隱蔽的房頂上張望臣缀。 院中可真熱鬧,春花似錦泻帮、人聲如沸精置。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脂倦。三九已至,卻和暖如春元莫,著一層夾襖步出監(jiān)牢的瞬間赖阻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工踱蠢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留火欧,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓茎截,卻偏偏與公主長得像苇侵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子企锌,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理衅檀,服務發(fā)現,斷路器霎俩,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,077評論 25 707
  • 計劃猿推! 1.早飯要清淡,燒餅加雞肉不能再吃了捌肴! 白煮蛋 水果 可以加個玉米或者白薯(這是最多的了) 2中午吃飯葷素...
    KUNbehappy閱讀 251評論 0 0
  • 前面10篇文章都在用命令行蹬叭,雖然裝逼不錯,但是我想說一句状知,平時我也是用source tree比較多點秽五,命令行一般都...
    轉角遇見一直熊閱讀 2,334評論 7 16
  • 開學前一天去了我媽閨蜜家里,和比我大三個月的小姐姐Y聊天饥悴。了解我的寶貝們肯定知道的坦喘,我不太喜歡叫哥哥姐姐的,不是不...
    不怕孤獨的貓閱讀 379評論 0 2