irpas技术客

iOS shareExtension总结分享_筱贰笔

网络 4902

非常感谢大家利用自己宝贵的时间来阅读我的文章 ,好久没有写东西了,这几天给项目添加苹果iOS8之后的shareExtension功能,踩了些坑,做下总结,希望后面的朋友们做这个功能的时候可以在这里一站式解决😄。希望这篇文章能给你的开发过程带来一些帮助。喜欢的可以关注一下我的简书、我的博客 ?? 关于shareExtension的基本功能和实现网上有很多资料,作者不想多说并向你扔了个传送门iOS Share Extension开发

下面主要说一下再整个过程中个人觉得比较重要的几个地方 #1、NSExtensionActivationRule配置

NSExtensionActivationSupportsAttachmentsWithMaxCount(附件最多限制)NSExtensionActivationSupportsAttachmentsWithMinCount(附件最少限制)NSExtensionActivationSupportsImageWithMaxCount(图片最多限制)NSExtensionActivationSupportsMovieWithMaxCount(视频最多限制)NSExtensionActivationSupportsWebPageWithMaxCount(Web页面最多限制)NSExtensionActivationSupportsWebURLWithMaxCount(Web链接最多限制)NSExtensionActivationSupportsFileWithMaxCount(文件最多限制)NSExtensionActivationSupportsText(是否支持文本类型)

这些属性根据自己的需求设置好数量就行。说一下可能遇到的问题 **1.1、**NSExtensionActivationSupportsText,主要用于备忘录之类文本分享,网上资料有的说设置bool类型,有的说设置string类型,值为YES,我这边都不好使,最后设置为值为1的number类型可以了。如果跟我遇到同样问题的可以试下 **1.2、**在App Store分享APP时,有两个NSExtensionItem,UTI分别为public.url和public.png,loadItemForTypeIdentifier获取item类型为NSURL和UIImage,截图分享的item类型也为UIImage #2、获取分享数据搭建UI 在自定义VC的viewDidLoad异步获取分享数据,获取完毕刷新UI

[MBProgressHUD showHUDAddedTo:self.view animated:YES]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{//异步获取分享内容 dispatch_group_t group = dispatch_group_create(); [self.extensionContext.inputItems enumerateObjectsUsingBlock:^(NSExtensionItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [obj.attachments enumerateObjectsUsingBlock:^(NSItemProvider * _Nonnull itemProvider, NSUInteger idx, BOOL * _Nonnull stop) { dispatch_group_enter(group); NSString *utiStr = @"要分享文件的UTI"; if ([itemProvider hasItemConformingToTypeIdentifier:utiStr]) { [itemProvider loadItemForTypeIdentifier:“utiStr” options:nil completionHandler:^(id<NSSecureCoding> _Nullable item, NSError * _Null_unspecified error) {//在这里保存获取到的分享数据 if ([(NSObject *)item isKindOfClass:[NSURL class]]){ } else if ([(NSObject *)item isKindOfClass:[UIImage class]]) {//截图||APP图片 }else if ([(NSObject *)item isKindOfClass:[NSString class]]) {//文本 } dispatch_group_leave(group); }]; } }]; }]; dispatch_group_notify(group, dispatch_get_main_queue(), ^{ //构建UI [self configUI]; }); });

#3、内存限制 这块是这个功能踩坑最多的地方,因为在widget中内存限制为120M,图片、视频、文件,随随便便都超了,这里说一下几个需要注意的地方 ##3.1图片处理 这里主要是多图时可能会出现内存过大的情况,我这边的处理是直接把图片copy到共享目录,在分享页展示的时候把压缩比例调大一点,然后跳转主App里发送原图。也可以直接将图片压缩后转成base64str数组然后写入共享目录,不过压缩比例不好控制,压缩后的总大小,小图不能压缩等都要考虑进去,下面把几个用到的方法和处理时机贴一下

**3.1.1、**图片压缩方法

- (UIImage *)resizeScaleImage:(CGFloat)scale { CGSize imgSize = self.size; CGSize targetSize = CGSizeMake(imgSize.width * scale, imgSize.height * scale); NSData *imageData = UIImageJPEGRepresentation(self, 1.0); CFDataRef data = (__bridge CFDataRef)imageData; CFStringRef optionKeys[1]; CFTypeRef optionValues[4]; optionKeys[0] = kCGImageSourceShouldCache; optionValues[0] = (CFTypeRef)kCFBooleanFalse; CFDictionaryRef sourceOption = CFDictionaryCreate(kCFAllocatorDefault, (const void **)optionKeys, (const void **)optionValues, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CGImageSourceRef imageSource = CGImageSourceCreateWithData(data, sourceOption); CFRelease(sourceOption); if (!imageSource) { NSLog(@"imageSource is Null!"); return nil; } //获取原图片属性 int imageSize = (int)MAX(targetSize.height, targetSize.width); CFStringRef keys[5]; CFTypeRef values[5]; //创建缩略图等比缩放大小,会根据长宽值比较大的作为imageSize进行缩放 keys[0] = kCGImageSourceThumbnailMaxPixelSize; CFNumberRef thumbnailSize = CFNumberCreate(NULL, kCFNumberIntType, &imageSize); values[0] = (CFTypeRef)thumbnailSize; keys[1] = kCGImageSourceCreateThumbnailFromImageAlways; values[1] = (CFTypeRef)kCFBooleanTrue; keys[2] = kCGImageSourceCreateThumbnailWithTransform; values[2] = (CFTypeRef)kCFBooleanTrue; keys[3] = kCGImageSourceCreateThumbnailFromImageIfAbsent; values[3] = (CFTypeRef)kCFBooleanTrue; keys[4] = kCGImageSourceShouldCacheImmediately; values[4] = (CFTypeRef)kCFBooleanTrue; CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CGImageRef thumbnailImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options); UIImage *resultImg = [UIImage imageWithCGImage:thumbnailImage]; CFRelease(thumbnailSize); CFRelease(options); CFRelease(imageSource); CFRelease(thumbnailImage); return resultImg; }

**3.1.2、**图片base64str互转

//图片转字符串 -(NSString *)imageToBase64Str { NSData *data = UIImageJPEGRepresentation(self, 1.0f); NSString *encodedImageStr = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; return encodedImageStr; } //字符串转图片 + (UIImage *)base64StrToUIImage:(NSString *)encodedImageStr { NSData *_decodedImageData = [[NSData alloc] initWithBase64Encoding:encodedImageStr]; UIImage *_decodedImage = [UIImage imageWithData:_decodedImageData]; return _decodedImage; }

3.1.3、获取主要信息loadItemForTypeIdentifier方法里保存图片地址,用于UI展示及后续写入共享目录

if ([itemProvider hasItemConformingToTypeIdentifier:@"public.image"]||[itemProvider hasItemConformingToTypeIdentifier:@"public.png"]) { [weakSelf.images addObject:((NSURL *)item).absoluteString]; }

**3.1.4、**确认分享时把图片批量copy到共享目录,也可以选择压缩后转base64str数组写入文件

NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.58.mismobile"]; NSMutableArray *imageStringData = [NSMutableArray array]; if (self.images.count > 0) { [self.images enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { @autoreleasepool { NSError *copyError; NSURL *fileURL = [groupURL URLByAppendingPathComponent:[((NSString *)obj).lastPathComponent stringByRemovingPercentEncoding]]; if ([[NSFileManager defaultManager] fileExistsAtPath:fileURL.absoluteString]) { [[NSFileManager defaultManager] removeItemAtPath:fileURL.absoluteString error:nil]; } NSRange fromRange = [((NSString *)obj) rangeOfString:@"/var"];//文件路径需要移除/var之前路径 NSString *fromPath = [((NSString *)obj) substringFromIndex:fromRange.location].stringByRemovingPercentEncoding; NSRange toRange = [fileURL.absoluteString rangeOfString:@"/var"];//文件路径需要移除/var之前路径 NSString *toPath = [fileURL.absoluteString substringFromIndex:toRange.location].stringByRemovingPercentEncoding; [[NSFileManager defaultManager] copyItemAtPath:fromPath toPath:toPath error:&copyError]; [imageStringData addObject:fileURL.absoluteString]; } }]; } if (self.shareImage) {//截图先写入文件,再存储地址 NSString *string = [NSString stringWithFormat:@"%f",[NSDate date].timeIntervalSince1970]; NSURL *fileURL = [groupURL URLByAppendingPathComponent:string]; NSData *imageData = UIImagePNGRepresentation(self.shareImage); [imageData writeToURL:fileURL atomically:YES]; [imageStringData addObject:fileURL.absoluteString]; } NSURL *imageInfoURL = [groupURL URLByAppendingPathComponent:[@"图片信息" stringByRemovingPercentEncoding]]; NSData *urlData = [NSJSONSerialization dataWithJSONObject:imageStringData options:NSJSONWritingPrettyPrinted error:nil]; [urlData writeToURL: imageInfoURL atomically:YES];

**3.1.5、**呼起主APP后先从获取图片地址数组,在通过图片地址取分享数据

// 把目标路径文件中的内容转化成NSData类型,加载(存储)到内存中 NSURL *dataURL = [[NSURL alloc] initWithString:imageStr]; NSData *data = [NSData dataWithContentsOfURL:(NSURL *)dataURL]; id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil]; NSArray *imageStringArray = (NSArray *)jsonObject; NSMutableArray *images = [NSMutableArray arrayWithCapacity:imageStringArray.count]; [imageStringArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:(NSString *)obj]]; [images addObject:[UIImage imageWithData:imageData]]; }]; if (images.count > 0) { //拿到image数据做后续处理 }

##3.2视频处理&文件处理 一方面是UI展示所需要的内容,如视频封面图、时长、大小,文件的大小、类型 这里说一下取文件大小时不能取NSData得length方式,大文件会导致内存溢出,应该使用NSFileManager获取,这里要注意下通过loadItemForTypeIdentifier取得的路径要先stringByRemovingPercentEncoding解码再截取/var之后的路径,比如你取得文件路径为file%3A%2F%2F%2Fprivate%2Fvar%2Fmobile%2FLibrary%2FMobile%20Documents%2Fcom%7Eapple%7ECloudDocs%2F%E6%9C%AA%E5%91%BD%E5%90%8D%E6%96%87%E4%BB%B6%E5%A4%B9%2Faaa.pdf 那你要用/var/mobile/Library/Mobile Documents/com~apple~CloudDocs/未命名文件夹/aaa.pdf去取文件大小,下面把主要用的方法贴一下

**3.2.1、**文件大小以及格式化

//单个文件的大小 + (NSString *) fileSizeAtPath:(NSString*) filePath{ NSFileManager *manager = [NSFileManager defaultManager]; if ([manager fileExistsAtPath:filePath]){ return [self sizeString:[[manager attributesOfItemAtPath:filePath error:nil] fileSize]]; } return @""; } + (NSString *)sizeString:(long long)size { CGFloat k = 1024.0; CGFloat fileSize = size; if (size < k) { // B fileSize = size; } else if (size < k * k) { // KB fileSize = size / k; } else { fileSize = size / (k * k); } if (size < k) { // B return [NSString stringWithFormat:@"%.2fB", fileSize]; } else if (size < k * k) { // KB return [NSString stringWithFormat:@"%.2fKB", fileSize]; } else { // M return [NSString stringWithFormat:@"%.2fM", fileSize]; } }

**3.2.2、**获取视频时长(这里要用全路径)

//获取视频时长 + (NSString *)getdurationTimeFromVideoPath:(NSURL*)fileURL { AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:fileURL options:nil]; CMTime durationTime = [asset duration]; NSUInteger duration = ceil(durationTime.value/durationTime.timescale); return [self formatTime:duration]; }

**3.2.3、**获取视频封面图(这里也要用全路径)

//获取视频一桢截图 + (UIImage *)getScreenShotImageFromVideoPath:(NSURL*)fileURL { UIImage *shotImage; AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:fileURL options:nil]; AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset]; gen.appliesPreferredTrackTransform = YES; CMTime time = CMTimeMakeWithSeconds(0.0, 600); NSError *error = nil; CMTime actualTime; CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error]; shotImage = [[UIImage alloc] initWithCGImage:image]; CGImageRelease(image); return shotImage; }

**3.2.4、**获取文件类型对应切图,把图片素材替换为你们UI给的切图

+ (UIImage *)fileIconForName:(NSString *)name { NSArray *videoTypeArray = @[@"mov", @"mp4", @"3gp", @"m4v"]; NSArray *audioTypeArray = @[@"aac", @"caf", @"mp3", @"m4a", @"wav", @"m4r"]; NSArray *pictureTypeArray = @[@"jpg", @"jpeg", @"gif", @"png", @"ico"]; NSArray *wordTypeArray = @[@"doc", @"docx", @"txt"]; NSString *suffix = name.pathExtension.lowercaseString; UIImage *image; if ([wordTypeArray containsObject:suffix]) { image = [UIImage imageNamed:@""]; } else if ([suffix isEqualToString:@"xls"] || [suffix isEqualToString:@"xlsx"]) { image = [UIImage imageNamed:@""]; } else if ([suffix isEqualToString:@"ppt"] || [suffix isEqualToString:@"pptx"]) { image = [UIImage imageNamed:@""]; } else if ([suffix isEqualToString:@"pdf"]) { image = [UIImage imageNamed:@""]; }else if ([suffix isEqualToString:@"zip"]) { image = [UIImage imageNamed:@""]; } else if ([suffix isEqualToString:@"rar"]) { image = [UIImage imageNamed:@""]; } else if ([videoTypeArray containsObject:suffix]) { image = [UIImage imageNamed:@""]; } else if ([audioTypeArray containsObject:suffix]) { image = [UIImage imageNamed:@""]; } else if ([pictureTypeArray containsObject:suffix]) { image = [UIImage imageNamed:@""]; } else { image = [UIImage imageNamed:@""]; } return image; }

##3.3大文件 因为需要把分享的文件存储到group共享目录里去,当文件超过120M的时候,就不能使用writeToURL了,要不会内存溢出,这里使用NSFileManager的copyItemAtPath,在使用这个函数的时候两个路径都要使用解码后再截取/var之后的路径

NSError *copyError; NSRange toRange = [fileURL.absoluteString rangeOfString:@"/var"];//文件路径需要移除/var之前路径 NSString *toPath = [fileURL.absoluteString substringFromIndex:toRange.location].stringByRemovingPercentEncoding; //这里我self.filePath保存时就做了处理了,所以只需处理toPath [[NSFileManager defaultManager] copyItemAtPath:self.filePath toPath:toPath error:&copyError]; if (copyError != nil) { NSLog(@"复制出错啦"); }

目前能想到的就这么多啦,如果有什么疑问或者发现什么不足,欢迎评论指正。


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #iOS