通过AirDrop发送一张图片或者一个文件,需要序列化实现了NSActivityItemSource的容器类实例,以NSData的数据格式传输。app发送/接收自定义的类型实例,还需要创建一个自定义的UTI,需要在Targets的info中添加两个节点:Document Types 和 Exported Type UTIs;
1、Document Types:
app注册此字段后可以接受自定义的UTI,同时这个字段也可以用来接收其他标准的UTI文件类型。
2、Exported Type UTIs:通过此字段向系统注册此app的自定义UTI。
发送被序列号容器类需要有两个选择:
1、把对象写入到遵循自定义UTI的文件中,把文件的路径作为url传入到UIActivityVIewController中。
2、序列化对象为NSData对象,把数据(NSData)对象传到UIActivityViewController,UIActivityViewController 会调用UIActivityItemSource的协议方法activityViewController:dataTypeIdentifierForActivityItemSource来确定自定义的UTI(将要使用的就是这种)。
当AirDrop接收到一个自定义UTI的文件时,它会寻找在系统中注册过改UTI的app,如果有多个app注册过改UTI,会显示一个选择列表。选中app后,该app的UIApplication的委托方法 application:openURL:sourceApplication:annotation 会被调用,在这个方法中处理接收到的文件,接收到的文件被保存在Document/Inbox文件夹中,app只有读取和删除的权限,建议copy到自定义的路径中,以便做增删改的操作。
发送:
- (IBAction)shareAction:(id)sender { // 传入的虽然时item的容器类实例,实际UIActivityViewController会调用UIActivityItemSource协议方法把该实例序列化为NSData // 可以从容器类 Profile 中看到明细。 UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[self.profile] applicationActivities:nil]; if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) { [self presentViewController:activityViewController animated:YES completion:nil]; } else { if(self.actionPopoverController.isPopoverVisible) { [self.actionPopoverController dismissPopoverAnimated:YES]; } else { self.actionPopoverController = [[UIPopoverController alloc] initWithContentViewController:activityViewController]; [self.actionPopoverController presentPopoverFromBarButtonItem:self.actionItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } } }
容器类Profile:
#import <Foundation/Foundation.h> @interface Profile : NSObject<NSSecureCoding, UIActivityItemSource> @property (nonatomic, copy) NSString *name; @property (nonatomic, strong) UIImage *image; @property (nonatomic, strong) UIImage *thumbnailImage; @property (nonatomic, copy) NSString *fileName; @property (nonatomic, assign) NSInteger imageContentMode; -(instancetype)initWithName:(NSString*)name image:(UIImage*)image; @end #import "Profile.h" #import "UIImage+resize.h" #import "Utilities.h" @implementation Profile // 初始化Profile的name和image -(instancetype)initWithName:(NSString *)name image:(UIImage *)image { self = [super init]; if(self) { _name = name; // 设置图片的尺寸 _image = [UIImage imageWithImage:image scaledToFitToSize:CGSizeMake(560, 470)]; } return self; } // 属性fileName如果不存在,构造一个随机的名字 -(NSString*)fileName { if (_fileName) { return _fileName; } _fileName = [NSString stringWithFormat:@"profile-%@.customprofile", [[[NSUUID UUID] UUIDString] substringWithRange:NSMakeRange(24, 12)]]; return _fileName; } // 设置image的同时设置thumbnailImage -(void)setImage:(UIImage *)image { _image = [UIImage imageWithImage:image scaledToFitToSize:CGSizeMake(560, 470)]; _thumbnailImage = [UIImage imageWithImage:image scaledToFitToSize:CGSizeMake(44, 44)]; } // thumbnailImage不存在,现场构造 -(UIImage*)thumbnailImage { if(_thumbnailImage) return _thumbnailImage; _thumbnailImage = [UIImage imageWithImage:_image scaledToFitToSize:CGSizeMake(44, 44)]; return _thumbnailImage; } #pragma mark - NSCoding // NSSecureCoding 继承自NSCoding,所以必须实现NSCoding,同时该类的实例能够序列化为NSData数据类型以便传输 -(void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:_name forKey:@"name"]; [aCoder encodeObject:_image forKey:@"image"]; [aCoder encodeObject:@(_imageContentMode) forKey:@"imageContentMode"]; } -(instancetype)initWithCoder:(NSCoder *)aDecoder { Profile *profile = nil; NSString *name = [aDecoder decodeObjectOfClass:[NSString class] forKey:@"name"]; UIImage *image = [aDecoder decodeObjectOfClass:[UIImage class] forKey:@"image"]; NSNumber *imageContentMode = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:@"imageContentMode"]; if(name && image && imageContentMode) { profile = [[Profile alloc] initWithName:name image:image]; profile.imageContentMode = imageContentMode.integerValue; } return profile; } #pragma mark - NSSecureCoding +(BOOL)supportsSecureCoding { return YES; } #pragma mark - UIActivityItemSource -(id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController { // 该类作为数据容器,最终是以NSData的数据类型发送的 return [NSData data]; } -(id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType { // 被序列化后的该对象作为item发送 return [Utilities securelyArchiveRootObject:self]; } -(NSString*)activityViewController:(UIActivityViewController *)activityViewController dataTypeIdentifierForActivityType:(NSString *)activityType { return @"com.hsun.customProfileUTI.customProfile"; } -(UIImage*)activityViewController:(UIActivityViewController *)activityViewController thumbnailImageForActivityType:(NSString *)activityType suggestedSize:(CGSize)size { UIImage *scaledImage; if (self.imageContentMode == UIViewContentModeScaleToFill) { scaledImage = [UIImage imageWithImage:self.image scaledToFillToSize:size]; } else { scaledImage = [UIImage imageWithImage:self.image scaledToFitToSize:size]; } return scaledImage; } @end
当同意接收AirDrop发送的数据后,若设备已经注册了该UTI的app,会用此app打开(多个的话会让选择),调用app的应用委托方法来处理接收的数据:
// 处理接收的数据 -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { if (url) { //当同意接受AirDrop发送的文件后,AirDrop会自动查询注册了UTI的app,找到之后就把文件放到 // Document/Inbox 文件夹下面(不存在该UTI的app则会自动跳转到ItuneStore搜做符合此UTI的app) // 读取接收到的文件,并处理(同接收URL处理的不同之处就是这里,从inbox中读取接收到的文件,这个文件是序列化后的数据文件) NSString *path = [url path]; Profile *profile = [Utilities securelyUnarchiveProfileWithPath:path]; if(profile) { //用接收到的数据做想做的事情。。。 } // 删除放在 Document/Inbox 下接收到的文件 [[NSFileManager defaultManager] removeItemAtPath:path error:nil]; } return YES; }