UIActivityViewController的相关用法(一)--URL的发送和接收
UIActivityViewController 是一个能为应用程序提供服务的试图控制器。操作系统已经提供了一些标准服务,诸如 复制内容到剪切板,提交内容到社交网站,通过 email 或 短信发送内容等。应用程序也可以自定义服务。在ios8中系统的一些应用可以直接通过action分享给qq好友和微信好友,是因为腾讯增加了Extension,第三方的app不注册腾讯的开发者是无法使用的。
UIActivityViewController 的Action分为两类,在UIAction的定义中可以查看到:
UIKIT_EXTERN NSString *const UIActivityTypePostToFacebook NS_AVAILABLE_IOS(6_0); UIKIT_EXTERN NSString *const UIActivityTypePostToTwitter NS_AVAILABLE_IOS(6_0); UIKIT_EXTERN NSString *const UIActivityTypePostToWeibo NS_AVAILABLE_IOS(6_0); // SinaWeibo UIKIT_EXTERN NSString *const UIActivityTypeMessage NS_AVAILABLE_IOS(6_0); UIKIT_EXTERN NSString *const UIActivityTypeMail NS_AVAILABLE_IOS(6_0); UIKIT_EXTERN NSString *const UIActivityTypePrint NS_AVAILABLE_IOS(6_0); UIKIT_EXTERN NSString *const UIActivityTypeCopyToPasteboard NS_AVAILABLE_IOS(6_0); UIKIT_EXTERN NSString *const UIActivityTypeAssignToContact NS_AVAILABLE_IOS(6_0); UIKIT_EXTERN NSString *const UIActivityTypeSaveToCameraRoll NS_AVAILABLE_IOS(6_0); UIKIT_EXTERN NSString *const UIActivityTypeAddToReadingList NS_AVAILABLE_IOS(7_0); UIKIT_EXTERN NSString *const UIActivityTypePostToFlickr NS_AVAILABLE_IOS(7_0); UIKIT_EXTERN NSString *const UIActivityTypePostToVimeo NS_AVAILABLE_IOS(7_0); UIKIT_EXTERN NSString *const UIActivityTypePostToTencentWeibo NS_AVAILABLE_IOS(7_0); UIKIT_EXTERN NSString *const UIActivityTypeAirDrop NS_AVAILABLE_IOS(7_0); typedef NS_ENUM(NSInteger, UIActivityCategory) { UIActivityCategoryAction, UIActivityCategoryShare, } NS_ENUM_AVAILABLE_IOS(7_0);
应用程序要复制配置、呈现和消除 UIActivityViewController。UIActivityViewContoller 的配置需要明确指出控制器要表现出的数据对象。
下面的代码源自于苹果官方提供的AirDropSample项目,按照本人理解的顺序解析,每个人理解的角度不一样,有偏差请以官方代码和释义为准。
一:通过UIActivityViewController发送/接收URL(文本等同之):
发送:用一个URLViewController来管理(
如果app用URL发送元数据,应该创建一个包含该url的容器类,此类必须要实现 UIActivityItemSource 协议。
)
#import <UIKit/UIKit.h> @interface UrlViewController : UIViewController @end #import "UrlViewController.h" #import "Utilities.h" #import "UrlContainer.h" @interface UrlViewController ()<UITextFieldDelegate> @property (weak, nonatomic) IBOutlet UITextField *textField; @property (strong, nonatomic) UrlContainer *urlContainer; @property (strong, nonatomic) UIPopoverController *popOverController; @end @implementation UrlViewController - (void)viewDidLoad { [super viewDidLoad]; // 加载收到的url, 如果没有收到的url返回nil NSURL *url = [Utilities loadUrl]; if (!url) { url = [NSURL URLWithString:@"hsun://test/one/two?key1=value1"]; } // NSLog(@"host:%@, user:%@, path:%@, fragment:%@, parameterString:%@, query:%@, relativePath:%@", url.host, url.user, url.path, url.fragment, url.parameterString, url.query, url.relativePath); // 初始化item容器 self.urlContainer = [[UrlContainer alloc] initWithUrl:url]; // 显示容器中的URL内容 self.textField.text = [self stringWithoutURLScheme:self.urlContainer.url]; } -(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // 为控制器注册通知,接收到URL, 保存窗口将要呈现 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadUrl) name:@"ReceiveURLViewController" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveWindowWillAppear) name:@"SaveWindowWillAppear" object:nil]; } -(void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; // view 消失时移除通知 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"ReceiveURLViewController" object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:@"SaveWindowWillAppear" object:nil]; } - (IBAction)share:(id)sender { // 用实现了 UIActivityItemSource协议的item容器初始化,容器类的定义在下面 UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[self.urlContainer] applicationActivities:nil]; /*** action 分为两种 : UIActivityCategoryAction, UIActivityCategoryShare 如果有不想展示的Share 或者 Action,可以通过如下实现 activityViewController.excludedActivityTypes = @[UIActivityTypePrint];
选择Action即可发送容器内的URL了,至此发送URL(文本)完成 ***/ // iphone 直接呈现 if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) { [self presentViewController:activityViewController animated:YES completion:nil]; } else // ipad 需要用PopOver { if([self.popOverController isPopoverVisible]) { [self.popOverController dismissPopoverAnimated:YES]; } else { self.popOverController = [[UIPopoverController alloc] initWithContentViewController:activityViewController]; [self.popOverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } } } #pragma mark - UITextFieldDelegate -(BOOL)textFieldShouldReturn:(UITextField *)textField { [self.textField resignFirstResponder]; return YES; } -(void)textFieldDidEndEditing:(UITextField *)textField { // 输入的如果不同于url容器中的内容,重新为其赋值 if(![self.textField.text isEqualToString:[self stringWithoutURLScheme:self.urlContainer.url]]) { NSString *urlString = [NSString stringWithFormat:@"hsun://%@", self.textField.text]; self.urlContainer.url = [NSURL URLWithString:urlString]; } } #pragma mark - String of url without scheme -(NSString *)stringWithoutURLScheme:(NSURL*)url { NSString *scheme = [[url scheme] stringByAppendingString:@"://"]; // 从scheme之后截取 return [[url absoluteString] substringFromIndex:scheme.length]; } #pragma mark - Handle received notifications -(void)reloadUrl { // 收到通知后加载收到的url NSURL *receivedUrl = [Utilities loadUrl]; if(receivedUrl) { self.urlContainer.url = receivedUrl; self.textField.text = [self stringWithoutURLScheme:receivedUrl]; } } -(void)saveWindowWillAppear { // 处理窗口将要呈现的通知 [self.textField resignFirstResponder]; } @end
关于实现UIActivityItemSource的URL(文本)容器类,
#import <Foundation/Foundation.h> @interface UrlContainer : NSObject<UIActivityItemSource> @property (nonatomic, strong) NSURL *url; -(instancetype)initWithUrl:(NSURL*)url; @end #import "UrlContainer.h" #import "UIImage+resize.h" @implementation UrlContainer -(instancetype)initWithUrl:(NSURL *)url { self = [super init]; if(self) { self.url = url; } return self; } #pragma mark - UIActivityItemSource -(id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController { // 占位,将要返回的类型 return self.url; } -(id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType { // 直接返回容器的url return self.url; } -(UIImage*)activityViewController:(UIActivityViewController *)activityViewController thumbnailImageForActivityType:(NSString *)activityType suggestedSize:(CGSize)size { // 为item提供缩略图,大小为建议的大小 // 用自定义的UIImage的Category resize 类方法处理图片为缩略图 return [UIImage imageWithImage:[UIImage imageNamed:@"Beads.png"] scaledToFitToSize:size]; } @end
自定义的UIImage+resize
#import <UIKit/UIKit.h> @interface UIImage (resize) +(UIImage*)imageWithImage:(UIImage*)image scaledToFitToSize:(CGSize)size; +(UIImage*)imageWithImage:(UIImage*)image scaledToFillToSize:(CGSize)size; @end #import "UIImage+resize.h" @implementation UIImage (resize) // 根据尺寸和矩形重绘图片 +(UIImage*)imageWithImage:(UIImage*)image scaledToSize:(CGSize)size inRect:(CGRect)rect { if ([UIScreen mainScreen].scale == 2.0) { UIGraphicsBeginImageContextWithOptions(size, YES, 2.0); } else { UIGraphicsBeginImageContext(size); } [image drawInRect:rect]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; } +(UIImage*)imageWithImage:(UIImage *)image scaledToFitToSize:(CGSize)size { // 如果图片原尺寸已经小于要Scaled尺寸,不作处理 if(image.size.height < size.height && image.size.width < size.width) { return [image copy]; } // 根据缩放后的宽高,得到缩放比例 CGFloat widthScaled = size.width / image.size.width; CGFloat heigtScaled = size.height / image.size.height; CGFloat scaledFactor = widthScaled > heigtScaled ? heigtScaled : widthScaled; // 缩放图片的尺寸 CGSize scaledSize = CGSizeMake(image.size.width * scaledFactor, image.size.height * scaledFactor); return [UIImage imageWithImage:image scaledToSize:scaledSize inRect:CGRectMake(0.0, 0.0, scaledSize.width, scaledSize.height)]; } +(UIImage*)imageWithImage:(UIImage *)image scaledToFillToSize:(CGSize)size { // 如果图片原尺寸已经小于要Scaled尺寸,不作处理 if(image.size.height < size.height && image.size.width < size.width) { return [image copy]; } // 根据缩放后的宽高,得到缩放比例 CGFloat widthScaled = size.width / image.size.width; CGFloat heigtScaled = size.height / image.size.height; CGFloat scaledFactor = widthScaled > heigtScaled ? heigtScaled : widthScaled; // 缩放图片的尺寸 CGSize scaledSize = CGSizeMake(image.size.width * scaledFactor, image.size.height * scaledFactor); CGPoint scaledPoint; widthScaled > heigtScaled ? (scaledPoint.y = (size.height - scaledSize.height)/2) : (scaledPoint.x = (size.width - scaledSize.height)/2); return [UIImage imageWithImage:image scaledToSize:size inRect:CGRectMake(scaledPoint.x, scaledPoint.y, scaledSize.width, scaledSize.height)]; } @end
接收:通过AirDrop接收URL(
要想接收一个自定义了scheme的URL,app需要注册为能接收这个scheme。在项目的Target下的Info节点,有URL Types,在此注册, 或者用代码注册,详细可参考此链接http://www.cocoachina.com/industry/20140522/8514.html
)
AirDrop接收到内容后会调用 UIApplication 的委托方法 application:openURL:sourceApplication:annotation,关于此方法的详细可以查看文档;
#import "AppDelegate.h" #import "RootTableViewController.h" #import "Utilities.h" @interface AppDelegate () @property (nonatomic, strong) UINavigationController *navController; @property (nonatomic, strong) RootTableViewController *rootTableViewController; @property (nonatomic, strong) UIWindow *saveReceivedWindow; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.navController = [[UIStoryboard storyboardWithName:@"Main.stroyboard" bundle:nil] instantiateInitialViewController]; if(self.navController) { self.rootTableViewController = [[self.navController viewControllers] firstObject]; } return YES; } -(void)applicationDidBecomeActive:(UIApplication *)application { NSLog(@"%@", NSStringFromSelector(_cmd)); } -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { if (url) { //在此处理接收到的url // 需要校验收到的是否是app需要的。saveReceivedWindow是为了接收数据类型而设的变量 if ([url.scheme isEqualToString:@"hsun"]) { // 保存url到文档中 [Utilities saveUrl:url]; // 广播收到URL的通知,如果UrlViewController处于显示状态则会收到此通知,然后重新从文档中加载新保存的url内容 [[NSNotificationCenter defaultCenter] postNotificationName:@"ReceiveURLViewController" object:nil]; //如果UrlViewController没有显示,调用根控制器显示 if ([self.navController.visibleViewController isEqual:self.rootTableViewController] && !self.saveReceivedWindow) { [self.rootTableViewController showUrlViewController]; } } } return YES; }