问题
iOS15之后,推送多条语音会产生多条横幅,对于动态金额语音,多条横幅是不可取的
解决方案
- 做版本管理,iOS15以上,用新的解决方案实现,iOS15以下还是沿用旧的推送方案
/// !!!!: 推送语音播报总控制逻辑 /// @param sourceURLsArr mp3源文件数组 /// @param bestAttemptContent /// @param completed -(void)pushVoiceNotificationWithWithSourceURLs:(NSArray *)sourceURLsArr bestAttemptContent:(UNMutableNotificationContent *)bestAttemptContent completed:(XSNotificationPushCompleted)completed { if (@available(iOS 15.0, *)) { // 合并音频文件生成新的音频 [self mergeAVAssetWithSourceURLs:sourceURLsArr completed:^(NSString *soundName, NSURL *soundsFileURL) { if (!soundName) { NSLog(@"声音生成失败!"); completed(); return; } #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 if (@available(iOS 15.0, *)) { bestAttemptContent.interruptionLevel = UNNotificationInterruptionLevelTimeSensitive; } #endif UNNotificationSound * sound = [UNNotificationSound soundNamed:soundName]; bestAttemptContent.sound = sound; completed(); }]; } else//iOS15以下,改用原来旧方式实现 { [self pushLocalNotificationIniOS14ToApp:0 withArray:sourceURLsArr completed:^{ completed(); }]; } }
- 新方案里面,通过NSFileManager把输出音频保存在【AppGroup】的/Library/Sounds/里面,坑点就是,AVAssetExportSession的输出路径必须要保证文件夹存在,不然会提示操作有误,当时直接通过contentsOfDirectoryAtPath来生成两个文件夹,结果不行, 必须要逐个生成,并且要留意生成的文件后缀要符合输出格式要求
///在AppGroup中合并音频 - (void)mergeAVAssetWithSourceURLs:(NSArray *)sourceURLsArr completed:(void (^)(NSString * soundName,NSURL * soundsFileURL)) completed{ //创建音频轨道,并获取多个音频素材的轨道 AVMutableComposition *composition = [AVMutableComposition composition]; //音频插入的开始时间,用于记录每次添加音频文件的开始时间 __block CMTime beginTime = kCMTimeZero; [sourceURLsArr enumerateObjectsUsingBlock:^(id _Nonnull audioFileURL, NSUInteger idx, BOOL * _Nonnull stop) { //获取音频素材 AVURLAsset *audioAsset1 = [AVURLAsset assetWithURL:[NSURL fileURLWithPath:audioFileURL]]; //音频轨道 AVMutableCompositionTrack *audioTrack1 = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:0]; //获取音频素材轨道 AVAssetTrack *audioAssetTrack1 = [[audioAsset1 tracksWithMediaType:AVMediaTypeAudio] firstObject]; //音频合并- 插入音轨文件 [audioTrack1 insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset1.duration) ofTrack:audioAssetTrack1 atTime:beginTime error:nil]; // 记录尾部时间 beginTime = CMTimeAdd(beginTime, audioAsset1.duration); }]; //用动态日期会占用空间 // NSDateFormatter *formater = [[NSDateFormatter alloc] init]; // [formater setDateFormat:@"yyyy-MM-dd-HH:mm:ss-SSS"]; // NSString * timeFromDateStr = [formater stringFromDate:[NSDate date]]; // NSString *outPutFilePath = [NSHomeDirectory() stringByAppendingFormat:@"/tmp/sound-%@.mp4", timeFromDateStr]; NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:kAppGroupID]; // NSURL * soundsURL = [groupURL URLByAppendingPathComponent:@"/Library/Sounds/" isDirectory:YES]; //建立文件夹 NSURL * soundsURL = [groupURL URLByAppendingPathComponent:@"Library/" isDirectory:YES]; if (![[NSFileManager defaultManager] contentsOfDirectoryAtPath:soundsURL.path error:nil]) { [[NSFileManager defaultManager] createDirectoryAtPath:soundsURL.path withIntermediateDirectories:YES attributes:nil error:nil]; } //建立文件夹 NSURL * soundsURL2 = [groupURL URLByAppendingPathComponent:@"Library/Sounds/" isDirectory:YES]; if (![[NSFileManager defaultManager] contentsOfDirectoryAtPath:soundsURL2.path error:nil]) { [[NSFileManager defaultManager] createDirectoryAtPath:soundsURL2.path withIntermediateDirectories:YES attributes:nil error:nil]; } // 新建文件名,如果存在就删除旧的 NSString * soundName = [NSString stringWithFormat:@"sound.m4a"]; NSString *outPutFilePath = [NSString stringWithFormat:@"Library/Sounds/%@", soundName]; NSURL * soundsFileURL = [groupURL URLByAppendingPathComponent:outPutFilePath isDirectory:NO]; // NSString * filePath = soundsURL.absoluteString; if ([[NSFileManager defaultManager] fileExistsAtPath:soundsFileURL.path]) { [[NSFileManager defaultManager] removeItemAtPath:soundsFileURL.path error:nil]; } //导出合并后的音频文件 //音频文件目前只找到支持m4a 类型的 AVAssetExportSession *session = [[AVAssetExportSession alloc]initWithAsset:composition presetName:AVAssetExportPresetAppleM4A]; // 音频文件输出 session.outputURL = soundsFileURL; session.outputFileType = AVFileTypeAppleM4A; //与上述的`present`相对应 session.shouldOptimizeForNetworkUse = YES; //优化网络 [session exportAsynchronouslyWithCompletionHandler:^{ if (session.status == AVAssetExportSessionStatusCompleted) { NSLog(@"合并成功----%@", outPutFilePath); if (completed) { completed(soundName,soundsFileURL); } } else { // 其他情况, 具体请看这里`AVAssetExportSessionStatus`. NSLog(@"合并失败----%ld", (long)session.status); if (completed) { completed(nil,nil); } } }]; }
- iOS15以下方案不变,通过循环递归推送多条语音信息来实现
////循环调用本地通知,播放音频文件 -(void)pushLocalNotificationIniOS14ToApp:(NSInteger)index withArray:(NSArray *)tmparray completed:(XSNotificationPushCompleted)completed{ __block NSInteger tmpindex = index; if(tmpindex < [tmparray count]){ NSString *audioFileURL = tmparray[tmpindex]; NSString * mp3Name = [audioFileURL lastPathComponent]; AVURLAsset *audioAsset=[AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:audioFileURL] options:nil]; //获取本地mpe3e文件时长 CMTime audioDuration = audioAsset.duration; float audioDurationSeconds = CMTimeGetSeconds(audioDuration); UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; //标题 content.sound = [UNNotificationSound soundNamed:[NSString stringWithFormat:@"%@",mp3Name]]; content.body = @""; // repeats,是否重复,如果重复的话时间必须大于60s,要不会报错 UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.01 repeats:NO]; /* */ //添加通知的标识符,可以用于移除,更新等搡作 NSString * identifier = [[NSUUID UUID] UUIDString]; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger]; [center addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error) { //第一条推送成功后,递归执行 float time = audioDurationSeconds+0.1; if (![mp3Name containsString:@"pre"]) { time = 0.4; } tmpindex = tmpindex+1; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, time * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [self pushLocalNotificationIniOS14ToApp:tmpindex withArray:tmparray completed:completed]; }); }]; }else{ completed(); } }