# publishImageAndVideoAnsRecord **Repository Path**: psppsp/publishImageAndVideoAnsRecord ## Basic Information - **Project Name**: publishImageAndVideoAnsRecord - **Description**: 因为公司有很多模块,几乎每个模块都需要发布视频、语言、照片。所以在很多库的基础上,搭建了一个集合,其中包括带placeHolder的TextView、录制小视频、录制音频、选择照片或拍照。其中包括了很多网络上的资源,只是做了集合,在此留个记录,也希望对一些有需求的朋友有帮助。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-06-30 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ImageAndVideoAnsRecord 效果图 ![](https://github.com/DayCrazy/publishImageAndVideoAnsRecord/blob/master/ImageFolder/ImageGif.gif) ##import "ViewController.m" PlaceholderTextView* titleTexView; // placeHolder的textView, PlaceholderTextView* contentTextView; showVideoView* showVideo; //添加照片、视频、语音按钮 使用 PlaceholderTextView titleTexView = [[PlaceholderTextView alloc]initWithFrame:CGRectMake(12, 10, kScreenWidth-24, 35)]; titleTexView.delegate = self; titleTexView.font = [UIFont systemFontOfSize:15.f]; titleTexView.textColor = [UIColor blackColor]; titleTexView.textAlignment = NSTextAlignmentLeft; titleTexView.editable = YES; titleTexView.placeholderColor = [UIColor darkTextColor]; titleTexView.placeholder = @"标题(必填)"; [showScrollView addSubview:titleTexView]; showVideoView showVideo = [[showVideoView alloc]initWithFrame:CGRectMake(0, contentTextView.frame.origin.y+contentTextView.frame.size.height+5, kScreenWidth, 78)]; [showScrollView addSubview:showVideo]; #pragma mark 🎱 发布前检查数据 包括各种视频、照片、语音的收集,方便上传服务器 - (BOOL)collectModelInformation ##import "showVideoView.h" @property (nonatomic,strong)NSMutableArray* imgeArray; // 图片数组 @property (nonatomic,strong)KZVideoModel* videoModel; // 视频Model @property (nonatomic,strong)KZVideoPlayer *player; //视频播放Player @property (nonatomic,strong)LGMessageModel *messageModel; //语音录制Model @property (nonatomic, strong) UIButton* addImageButton; //添加照片按钮 @property (nonatomic, strong) UIButton* addVideoButton; //添加视频按钮 @property (nonatomic, strong) UIButton* addRecordButton; //添加语音按钮 ##import "showVideoView.m" @property (nonatomic, assign) BOOL isVideo; //是否已添加视频 @property (nonatomic, assign) BOOL isPhoto; //是否已添加照片 @property (nonatomic, assign) BOOL isRecord; //是否已添加语音 在点击按钮时改变对应的is属性,在set方法中对相应的按钮位置更新 ###照片 #pragma mark 🎱 添加照片 默认6张,可以修改 - (void)addImageButtonAction:(UIButton*)sender #pragma mark 🎱 显示照片 照片在Collection中显示,方便完成增删 collectionCell 中有 普通照片、添加照片按钮、添加视频按钮,根据CircleCellModel中cirModelState属性判断 对应的方法在其viewController里实现 - (void)addImageToshowCirclectionView:(NSArray*)imageArray{ circleCollectionView.hidden = NO; CGFloat circleCollectionHeight = 60; if (photoNumber >0) { //不是第一次添加照片 photoNumber = photoNumber + imageArray.count; [self deleteAddPhotoButton]; // [self delete_addVideoButton]; //没有视频就是3张照片,若有视频就是2张 if (photoNumber > 3) { // circleCollection变两行,刷新tableview // superViewController.photoHeight = 220; circleCollectionHeight = collectionViewHeight*2; self.height = self.height + collectionViewHeight; // [self changeSuperScrollViewContentSize:self.height]; if (photoNumber >= 6) { //多次添加照片总数超过限制,截取7-self.imgeArray.count个元素拼接 NSRange range = NSMakeRange(0, 6 - circleCollectionView.photoArray.count); NSArray* tempArray = [imageArray subarrayWithRange:range]; [self.imgeArray addObjectsFromArray:tempArray]; for (int i = 0; i < tempArray.count; i++) { CircleCellModel *circelModel = [[CircleCellModel alloc]init]; if ([tempArray[i] isKindOfClass:[ZZPhoto class]]) { ZZPhoto* model = tempArray[i]; circelModel.photoImage = model.originImage; }else if([tempArray[i] isKindOfClass:[ZZCamera class]]){ ZZCamera* model = tempArray[i]; circelModel.photoImage = model.image; }else{ circelModel.photoImage = tempArray[i]; } circelModel.state = CirModelStateNormal; [circleCollectionView.photoArray addObject:circelModel]; } photoNumber = 6; }else{ [self.imgeArray addObjectsFromArray:imageArray]; for (int i = 0; i < imageArray.count; i++) { CircleCellModel *circelModel = [[CircleCellModel alloc]init]; if ([imageArray[i] isKindOfClass:[ZZPhoto class]]) { ZZPhoto* model = imageArray[i]; circelModel.photoImage = model.originImage; }else if([imageArray[i] isKindOfClass:[ZZCamera class]]){ ZZCamera* model = imageArray[i]; circelModel.photoImage = model.image; }else{ circelModel.photoImage = imageArray[i]; } circelModel.state = CirModelStateNormal; [circleCollectionView.photoArray addObject:circelModel]; } //最后放一张pluse CircleCellModel *cirModel = [[CircleCellModel alloc]init]; cirModel.photoImage = [UIImage imageNamed:@"plus"]; cirModel.state = CirModelStateAdd; [circleCollectionView.photoArray addObject:cirModel]; } }else{ [self.imgeArray addObjectsFromArray:imageArray]; for (int i = 0; i < imageArray.count; i++) { CircleCellModel *circelModel = [[CircleCellModel alloc]init]; if ([imageArray[i] isKindOfClass:[ZZPhoto class]]) { ZZPhoto* model = imageArray[i]; circelModel.photoImage = model.originImage; }else if([imageArray[i] isKindOfClass:[ZZCamera class]]){ ZZCamera* model = imageArray[i]; circelModel.photoImage = model.image; }else{ circelModel.photoImage = imageArray[i]; } circelModel.state = CirModelStateNormal; [circleCollectionView.photoArray addObject:circelModel]; } //最后放一张pluse CircleCellModel *cirModel = [[CircleCellModel alloc]init]; cirModel.photoImage = [UIImage imageNamed:@"plus"]; cirModel.state = CirModelStateAdd; [circleCollectionView.photoArray addObject:cirModel]; } }else{ //第一次添加照片 photoNumber = photoNumber + imageArray.count; [circleCollectionView.photoArray removeAllObjects]; if(self.imgeArray.count <=0){ [self.imgeArray addObjectsFromArray:imageArray]; } //没有视频就是3张照片,若有视频就是2张 if (photoNumber > 3) { // circleCollection变两行,刷新tableview // superViewController.photoHeight = 220; circleCollectionHeight = collectionViewHeight*2; } for (int i = 0; i < self.imgeArray.count; i++) { CircleCellModel *circelModel = [[CircleCellModel alloc]init]; if ([imageArray[i] isKindOfClass:[ZZPhoto class]]) { ZZPhoto* model = imageArray[i]; circelModel.photoImage = model.originImage; }else if([imageArray[i] isKindOfClass:[ZZCamera class]]){ ZZCamera* model = imageArray[i]; circelModel.photoImage = model.image; }else{ circelModel.photoImage = imageArray[i]; } circelModel.state = CirModelStateNormal; [circleCollectionView.photoArray addObject:circelModel]; } if ( photoNumber < 6 ) { //最后放一张pluse CircleCellModel *cirModel = [[CircleCellModel alloc]init]; cirModel.photoImage = [UIImage imageNamed:@"plus"]; cirModel.state = CirModelStateAdd; [circleCollectionView.photoArray addObject:cirModel]; }else{ // if (photoNumber >= 7) { //多次添加照片总数超过限制,截取7-self.imgeArray.count个元素拼接 NSRange range = NSMakeRange(0, 6 - circleCollectionView.photoArray.count); NSArray* tempArray = [imageArray subarrayWithRange:range]; [self.imgeArray addObjectsFromArray:tempArray]; for (int i = 0; i < tempArray.count; i++) { CircleCellModel *circelModel = [[CircleCellModel alloc]init]; if ([tempArray[i] isKindOfClass:[ZZPhoto class]]) { ZZPhoto* model = tempArray[i]; circelModel.photoImage = model.originImage; }else if([tempArray[i] isKindOfClass:[ZZCamera class]]){ ZZCamera* model = tempArray[i]; circelModel.photoImage = model.image; }else{ circelModel.photoImage = tempArray[i]; } circelModel.state = CirModelStateNormal; [circleCollectionView.photoArray addObject:circelModel]; } } self.height = self.height + circleCollectionHeight; self.isPhoto = YES; } // [self chanageCircleCollectonFrame]; [circleCollectionView mas_updateConstraints:^(MASConstraintMaker *make) { make.height.mas_equalTo(circleCollectionHeight); if (_isRecord) { make.top.equalTo(playButton.mas_bottom).offset(10); }else{ make.top.equalTo(self.mas_top); } }]; // [self refreshButtonFrame]; [circleCollectionView reloadData]; } ###视频 #pragma mark 🎱添加视频 - (void)_addVideoButtonAction:(UIButton*)sender{ KZVideoViewController *videoVC = [[KZVideoViewController alloc] init]; videoVC.delegate = self; [videoVC startAnimationWithType:KZVideoViewShowTypeSingle]; } 实现KZVideoViewControllerDelegate #pragma mark 🎱 视频录制或选择后回调 - (void)videoViewController:(KZVideoViewController *)videoController didRecordVideo:(KZVideoModel *)videoModel{ // [self refreshButtonFrame]; // _addVideoButton.hidden = YES; // [self delete_addVideoButton]; [circleCollectionView reloadData]; _videoModel = videoModel; [self playerVideo:videoModel]; } #pragma mark 🎱 视频播放 - (void)playerVideo:(KZVideoModel*)videoModel{ NSURL *videoUrl = [NSURL fileURLWithPath:videoModel.videoAbsolutePath]; if (_player == nil) { if (videoUrl) { _player = [[KZVideoPlayer alloc] initWithFrame:CGRectMake(12, self.top, 102, 80) videoUrl:videoUrl]; } } _player.layer.masksToBounds = NO; _player.backgroundColor = [UIColor blackColor]; [self.superview addSubview:_player]; [_player mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.mas_left).offset(12); make.top.equalTo(self.mas_top); make.width.mas_equalTo(102); make.height.mas_equalTo(80); }]; UIButton* deletVideo = [[UIButton alloc]initWithFrame:CGRectMake(_player.width-20 , 0, 20, 20)]; [deletVideo setImage:[UIImage imageNamed:@"delete28"] forState:UIControlStateNormal]; [_player addSubview:deletVideo]; [deletVideo addTarget:self action:@selector(deleteVideoButtonAction:) forControlEvents:UIControlEventTouchUpInside]; // [self chanageCircleCollectonFrame]; self.isVideo = YES; } ###语音  定时60S,可修改      //录音储存路径   #define DocumentPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; _addRecordButton = [[UIButton alloc]initWithFrame:CGRectMake(_addVideoButton.right+5, _addVideoButton.top, _addVideoButton.width, _addVideoButton.height)]; [_addRecordButton setImage:[UIImage imageNamed:@"second_sounding_d"] forState:UIControlStateNormal]; [_addRecordButton addTarget:self action:@selector(startRecordVoice) forControlEvents:UIControlEventTouchDown]; // 开始录音 [_addRecordButton addTarget:self action:@selector(cancelRecordVoice) forControlEvents:UIControlEventTouchUpOutside];// 取消录音 [_addRecordButton addTarget:self action:@selector(confirmRecordVoice) forControlEvents:UIControlEventTouchUpInside];// 录音结束 [_addRecordButton addTarget:self action:@selector(updateCancelRecordVoice) forControlEvents:UIControlEventTouchDragExit]; // 更新录音显示状态,手指向上滑动后 提示松开取消录音 [_addRecordButton addTarget:self action:@selector(updateContinueRecordVoice) forControlEvents:UIControlEventTouchDragEnter]; //更新录音状态,手指重新滑动到范围内,提示向上取消录音 将生成的caf格式的视频转换成mp3格式,方便与安卓共用,但是会有比较严重的失真,如果安卓不是需求尽量不要转 - (NSString*)formatConversionToMp3{ NSString *cafFilePath = self.messageModel.soundFilePath; //caf文件路径 NSString* fileName = [NSString stringWithFormat:@"/voice-%5.2f.mp3", [[NSDate date] timeIntervalSince1970] ];//存储mp3文件的路径 NSString *mp3FilePath = [[DocumentPath stringByAppendingPathComponent:@"SoundFile"] stringByAppendingPathComponent:fileName]; @try { CGFloat read, write; FILE *pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb"); //source 被转换的音频文件位置 fseek(pcm, 4*1024, SEEK_CUR); //skip file header FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb"); //output 输出生成的Mp3文件位置 const int PCM_SIZE = 8192; const int MP3_SIZE = 8192; short int pcm_buffer[PCM_SIZE*2]; unsigned char mp3_buffer[MP3_SIZE]; lame_t lame = lame_init(); lame_set_in_samplerate(lame, 11025.0); lame_set_VBR(lame, vbr_default); lame_init_params(lame); do { read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm); if (read == 0) write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE); else write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE); fwrite(mp3_buffer, write, 1, mp3); } while (read != 0); lame_close(lame); fclose(mp3); fclose(pcm); } @catch (NSException *exception) { NSLog(@"%@",[exception description]); } @finally { } // 将caf 文件删除 [[NSFileManager defaultManager] removeItemAtPath:self.messageModel.soundFilePath error:nil]; NSLog(@"mp3FilePath :: :::::≥≥≥®%@",mp3FilePath); return mp3FilePath; } #TIP: 之前设定的是照片和视频不能同时发布,但是布局和逻辑已经做好了,如果需要可以修改showVideoView.m中 - (void)setIsPhoto:(BOOL)isPhoto; //添加视频按钮可以使用 _addVideoButton.enabled = NO; [_addVideoButton setImage:[UIImage imageNamed:@"second_video_s"] forState:UIControlStateNormal]; 注释掉就可以了