短视频软件开发,仿抖音滑动播放视频的实现
下面要说的就是短视频软件开发重中之重,仿抖音滑动播放视频的实现。
当我们首次进入播放短视频页面时,会优先判断当前的视频列表videoList是否有值,如果没有值或当前的视频的index大于videoList.count - 3 时,就会重新请求服务端,获取新的一组短视频。
下面时核心代码
if (!_videoList || _videoList.count == 0) {_isHome = YES;_currentIndex = 0;_pages = 1;self.videoList = [NSMutableArray array];[self requestMoreVideo];//请求数据并加载页面
}
if (_currentIndex>=_videoList.count-3) {_pages += 1;[self requestMoreVideo];//请求数据并加载页面}
- (void)requestMoreVideo { WeakSelf;[YBNetworking postWithUrl:url Dic:nil Suc:^(NSDictionary *data, NSString *code, NSString *msg) {if ([code isEqual:@"0"]) {NSArray *info = [data valueForKey:@"info"];if (_pages==1) {[_videoList removeAllObjects];}[_videoList addObjectsFromArray:info];if (_isHome == YES) {_isHome = NO;_scrollViewOffsetYOnStartDrag = -100;[weakSelf scrollViewDidEndScrolling];//加载页面}}} Fail:^(id fail) {}];
}
结下来我们要介绍加载页面的三种情况,这里我们会用到三个UIImageView,为firstImageView、secondImageView,thirdImageView,对应三个展示UI的View,分别为firstFront、secondFront、thirdFront,对应三个数据源lastHostDic、hostdic、nextHostDic:
第一种是刚进来currentIndex == 0(currentIndex是指当前滚动到第几个视频),这时候我们要设置UIScrollView的ContentOffset为(0,0), currentPlayerIV(当前UIIMageView)为firstImageView,currentFront(当前呈现UI的View)为firstFront。并且要预加载secondImageView的数据,这里不用处理thirdImageView,因为只能向下滑,不需要预加载thirdImageView并且滚到第二个的时候自然给第三个赋值:
//第一个[self.backScrollView setContentOffset:CGPointMake(0, 0) animated:NO];_currentPlayerIV = _firstImageView;_currentFront = _firstFront;/*** _currentIndex=0时,重新处理下_secondImageView的封面、* 不用处理_thirdImageView,因为滚到第二个的时候上面的判断自然给第三个赋值*/[_firstImageView sd_setImageWithURL:[NSURL URLWithString:minstr([_hostdic valueForKey:@"thumb"])]];[self setUserData:_hostdic withFront:_firstFront];[self setVideoData:_hostdic withFront:_firstFront];[_secondImageView sd_setImageWithURL:[NSURL URLWithString:minstr([_nextHostDic valueForKey:@"thumb"])]];[self setUserData:_nextHostDic withFront:_secondFront];[self setVideoData:_nextHostDic withFront:_secondFront];
这里的setUerData和setVideoData是给页面加载数据的,详细实现为:
-(void)setUserData:(NSDictionary *)dataDic withFront:(FrontView*)front{NSDictionary *musicDic = [dataDic valueForKey:@"musicinfo"];id userinfo = [dataDic valueForKey:@"userinfo"];NSString *dataUid;NSString *dataIcon;NSString *dataUname;if ([userinfo isKindOfClass:[NSDictionary class]]) {dataUid = [NSString stringWithFormat:@"%@",[userinfo valueForKey:@"id"]];dataIcon = [NSString stringWithFormat:@"%@",[userinfo valueForKey:@"avatar"]];//右边最上面的带➕的头像图片dataUname = [NSString stringWithFormat:@"@%@",[userinfo valueForKey:@"user_nicename"]];//左下角第一行@的作者名}else{dataUid = @"0";dataIcon = @"";dataUname = @"";}NSString *musicID = [NSString stringWithFormat:@"%@",[musicDic valueForKey:@"id"]];NSString *musicCover = [NSString stringWithFormat:@"%@",[musicDic valueForKey:@"img_url"]];//musicIV右下角转动的唱片上覆盖的歌曲背景图片if ([musicID isEqual:@"0"]) {[front.musicIV sd_setImageWithURL:[NSURL URLWithString:_hosticon]];}else{[front.musicIV sd_setImageWithURL:[NSURL URLWithString:musicCover]];}[front setMusicName:[NSString stringWithFormat:@"%@",[musicDic valueForKey:@"music_format"]]];front.titleL.text = [NSString stringWithFormat:@"%@",[dataDic valueForKey:@"title"]];//左下角滚动的文字front.nameL.text = dataUname;[front.iconBtn sd_setBackgroundImageWithURL:[NSURL URLWithString:dataIcon] forState:UIControlStateNormal placeholderImage:[UIImage imageNamed:@"default_head.png"]];//广告NSString *is_ad_str = [NSString stringWithFormat:@"%@",[dataDic valueForKey:@"is_ad"]];NSString *ad_url_str = [NSString stringWithFormat:@"%@",[dataDic valueForKey:@"ad_url"]];CGFloat ad_img_w = 0;if (![PublicObj checkNull:ad_url_str]&&[is_ad_str isEqual:@"1"]&&![PublicObj checkNull:front.titleL.text]) {NSString *att_text = [NSString stringWithFormat:@"%@ ",front.titleL.text];UIImage *ad_link_img = [UIImage imageNamed:@"广告-详情"];NSMutableAttributedString *att_img = [NSMutableAttributedString yy_attachmentStringWithContent:ad_link_img contentMode:UIViewContentModeCenter attachmentSize:CGSizeMake(13, 13) alignToFont:SYS_Font(15) alignment:YYTextVerticalAlignmentCenter];NSMutableAttributedString *title_att = [[NSMutableAttributedString alloc]initWithString:att_text];//NSLog(@"-==-:%@==:%@==img:%@",att_text,title_att,att_img);[title_att appendAttributedString:att_img];NSRange click_range = [[title_att string] rangeOfString:[att_img string]];title_att.yy_font = SYS_Font(15);title_att.yy_color = [UIColor whiteColor];title_att.yy_lineBreakMode = NSLineBreakByTruncatingHead;title_att.yy_kern = [NSNumber numberWithFloat:0.2];[title_att addAttribute:NSBackgroundColorAttributeName value:[UIColor clearColor] range:click_range];[title_att yy_setTextHighlightRange:click_range color:[UIColor clearColor] backgroundColor:[UIColor clearColor] tapAction:^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) {//[YBMsgPop showPop:@"1111111"];[self adJump:ad_url_str];}];front.titleL.preferredMaxLayoutWidth =_window_width*0.75;front.titleL.attributedText = title_att;ad_img_w = 30;}//计算名称长度 最长3行高度最大60CGSize titleSize = [front.titleL.text boundingRectWithSize:CGSizeMake(_window_width*0.75, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:SYS_Font(15)} context:nil].size;CGFloat title_h = titleSize.height>60?60:titleSize.height;CGFloat title_w = _window_width*0.75;//titleSize.width>=(_window_width*0.75)?titleSize.width:titleSize.width+ad_img_w;front.titleL.frame = CGRectMake(0, front.musicL.top-title_h, title_w, title_h);front.nameL.frame = CGRectMake(0, front.titleL.top-25, front.botView.width, 25);front.followBtn.frame = CGRectMake(front.iconBtn.left+12, front.iconBtn.bottom-13, 26, 26);//广告if ([is_ad_str isEqual:@"1"]) {front.adLabel.hidden = NO;front.adLabel.frame = CGRectMake(0, front.nameL.top-25, 45, 20);}else{front.adLabel.hidden = YES;}}
-(void)setVideoData:(NSDictionary *)videoDic withFront:(FrontView*)front{_shares =[NSString stringWithFormat:@"%@",[videoDic valueForKey:@"shares"]];_likes = [NSString stringWithFormat:@"%@",[videoDic valueForKey:@"likes"]];_islike = [NSString stringWithFormat:@"%@",[videoDic valueForKey:@"islike"]];_comments = [NSString stringWithFormat:@"%@",[videoDic valueForKey:@"comments"]];NSString *isattent = [NSString stringWithFormat:@"%@",[NSString stringWithFormat:@"%@",[videoDic valueForKey:@"isattent"]]];//_steps = [NSString stringWithFormat:@"%@",[info valueForKey:@"steps"]];WeakSelf;//dispatch_async(dispatch_get_main_queue(), ^{//点赞数 评论数 分享数if ([weakSelf.islike isEqual:@"1"]) {[front.likebtn setImage:[UIImage imageNamed:@"home_zan_sel"] forState:0];//weakSelf.likebtn.userInteractionEnabled = NO;} else{[front.likebtn setImage:[UIImage imageNamed:@"home_zan"] forState:0];//weakSelf.likebtn.userInteractionEnabled = YES;}[front.likebtn setTitle:[NSString stringWithFormat:@"%@",_likes] forState:0];front.likebtn = [PublicObj setUpImgDownText:front.likebtn];[front.enjoyBtn setTitle:[NSString stringWithFormat:@"%@",_shares] forState:0];front.enjoyBtn = [PublicObj setUpImgDownText:front.enjoyBtn];[front.commentBtn setTitle:[NSString stringWithFormat:@"%@",_comments] forState:0];front.commentBtn = [PublicObj setUpImgDownText:front.commentBtn];if ([[Config getOwnID] isEqual:weakSelf.hostid] || [isattent isEqual:@"1"]) {front.followBtn.hidden = YES;}else{[front.followBtn setImage:[UIImage imageNamed:@"home_follow"] forState:0];front.followBtn.hidden = NO;[front.followBtn.layer addAnimation:[PublicObj followShowTransition] forKey:nil];}//});
}
第二种是当你用手滑动的时候,currentIndex > 0 并且小于videoList.count - 1(即既不是第一个也不是最后一个视频),这时候会优先触发代理方法:
#pragma mark - scrollView delegate
//开始拖拽
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{lastContenOffset = scrollView.contentOffset.y;//NSLog(@"111=====%f",scrollView.contentOffset.y);_currentPlayerIV.jp_progressView.hidden = YES;//当前播放进度隐藏self.scrollViewOffsetYOnStartDrag = scrollView.contentOffset.y;//记录开始拖拽的contentoffset
}
//结束拖拽
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollViewwillDecelerate:(BOOL)decelerate {endDraggingOffset = scrollView.contentOffset.y;//记录结束拖拽的位置//NSLog(@"222=====%f",scrollView.contentOffset.y);if (decelerate == NO) {[self scrollViewDidEndScrolling];}
}//开始减速
-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {scrollView.scrollEnabled = NO;//NSLog(@"333=====%f",scrollView.contentOffset.y);}- (void)scrollViewDidScroll:(UIScrollView *)scrollView{//NSLog(@"currentIndex=====%.2f",scrollView.contentSize.height);
}//结束减速
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {scrollView.scrollEnabled = YES;//NSLog(@"444=====%f",scrollView.contentOffset.y);if (lastContenOffset < scrollView.contentOffset.y && (scrollView.contentOffset.y-lastContenOffset)>=_window_height) {NSLog(@"=====向上滚动=====");_currentIndex++;if (_currentIndex>_videoList.count-1) {_currentIndex =_videoList.count-1;}}else if(lastContenOffset > scrollView.contentOffset.y && (lastContenOffset-scrollView.contentOffset.y)>=_window_height){NSLog(@"=====向下滚动=====");_currentIndex--;if (_currentIndex<0) {_currentIndex=0;}}else{NSLog(@"=======本页拖动未改变数据=======");if (scrollView.contentOffset.y == 0 && _currentIndex==0) {[YBMsgPop showPop:@"已经到顶了哦^_^"];}else if (scrollView.contentOffset.y == _window_height*2 && _currentIndex==_videoList.count-1){[YBMsgPop showPop:@"没有更多了哦^_^"];}}_currentPlayerIV.jp_progressView.hidden = NO;[self scrollViewDidEndScrolling];if (_requestUrl) {if (_currentIndex>=_videoList.count-3) {_pages += 1;[self requestMoreVideo];}}}#pragma mark - Private- (void)scrollViewDidEndScrolling {if((self.scrollViewOffsetYOnStartDrag == self.backScrollView.contentOffset.y) && (endDraggingOffset!= _scrollViewOffsetYOnStartDrag)){return;}//NSLog(@"7-8==%f====%f",self.scrollViewOffsetYOnStartDrag,self.backScrollView.contentOffset.y);[self changeRoom];}
这时当scrollview 滑动自动触发翻页时,则让UIScrollView迅速复位,这时候我们要设置UIScrollView的ContentOffset为(0,_window_height), _window_height为当前屏幕大小,currentPlayerIV(当前UIIMageView)为secondImageView,currentFront(当前呈现UI的View)为secondFront。并且要预加载firstImageView,firstFront和thirdImageView,thirdFront数据的。代码如下:
[_secondImageView sd_setImageWithURL:[NSURL URLWithString:minstr([_hostdic valueForKey:@"thumb"])]];//这里设置视频的第一帧,用于在整个页面显示[self setUserData:_hostdic withFront:_secondFront];[self setVideoData:_hostdic withFront:_secondFront];
[self.backScrollView setContentOffset:CGPointMake(0, _window_height) animated:NO];_currentPlayerIV = _secondImageView;_currentFront = _secondFront;
if (_curentIndex>0) {_lastHostDic = _videoList[_curentIndex-1];[_firstImageView sd_setImageWithURL:[NSURL URLWithString:minstr([_lastHostDic valueForKey:@"thumb"])]];[self setUserData:_lastHostDic withFront:_firstFront];[self setVideoData:_lastHostDic withFront:_firstFront];}if (_curentIndex < _videoList.count-1) {_nextHostDic = _videoList[_curentIndex+1];[_thirdImageView sd_setImageWithURL:[NSURL URLWithString:minstr([_nextHostDic valueForKey:@"thumb"])]];[self setUserData:_nextHostDic withFront:_thirdFront];[self setVideoData:_nextHostDic withFront:_thirdFront];}
第三种情况是滚动到最后一个,这时候我们要设置UIScrollView的ContentOffset为(0,_window_height*2), _window_height为当前屏幕大小,currentPlayerIV(当前UIIMageView)为thirdImageView,currentFront(当前呈现UI的View)为thirdFront。并且要预加载secondImageView,secondFront数据的。代码如下:
//最后一个[self.backScrollView setContentOffset:CGPointMake(0, _window_height*2) animated:NO];_currentPlayerIV = _thirdImageView;_currentFront = _thirdFront;/*** _currentIndex=_videoList.count-1时,重新处理下_secondImageView的封面、* 这个时候只能上滑 _secondImageView 给 _lastHostDic的值*/[_secondImageView sd_setImageWithURL:[NSURL URLWithString:minstr([_lastHostDic valueForKey:@"thumb"])]];[self setUserData:_lastHostDic withFront:_secondFront];[self setVideoData:_lastHostDic withFront:_secondFront];[_thirdImageView sd_setImageWithURL:[NSURL URLWithString:minstr([_hostdic valueForKey:@"thumb"])]];[self setUserData:_hostdic withFront:_thirdFront];[self setVideoData:_hostdic withFront:_thirdFront];
当三种情况都介绍完后就涉及到短视频软件开发最终的播放了,这里我使用的是JPVideoPlayer播放器(完全开源的,有兴趣的可以自行下载研究原理),播放的主要代码如下:
//切记一定要先把当前播放的上一个关闭[_currentPlayerIV jp_stopPlay];//开始播放[_currentPlayerIV jp_playVideoMuteWithURL:[NSURL URLWithString:_playUrl]bufferingIndicator:[JPBufferView new]progressView:[JPLookProgressView new]configuration:^(UIView *view, JPVideoPlayerModel *playerModel) {view.jp_muted = NO;//播放器的音频输出是否静音_firstWatch = YES;if (_currentPlayerIV.image.size.width>0 && (_currentPlayerIV.image.size.width >= _currentPlayerIV.image.size.height)) {playerModel.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;}else{playerModel.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;}}];
下面有几个需要设置的重要代理
1)实现重复播放
//return返回NO以防止重播视频。 返回YES则重复播放视频。如果未实施,则默认为YES
- (BOOL)shouldAutoReplayForURL:(nonnull NSURL *)videoURL
{return YES;
}
2)播放状态改变时需要做的相应处理,主要是页面消失的时候停止播放
//播放状态改变的时候触发
-(void)playerStatusDidChanged:(JPVideoPlayerStatus)playerStatus {NSLog(@"=====7-8====%lu",(unsigned long)playerStatus);if (_stopPlay == YES) {NSLog(@"8-4:play-停止了");_stopPlay = NO;_firstWatch = NO;//页面已经消失了,就不要播放了[_currentPlayerIV jp_stopPlay];}if (playerStatus == JPVideoPlayerStatusPlaying) {if (_bufferIV) {dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{[_bufferIV removeFromSuperview];});}}if (playerStatus == JPVideoPlayerStatusReadyToPlay && _firstWatch==YES) {//addview}if (playerStatus == JPVideoPlayerStatusStop && _firstWatch == YES) {//finish_firstWatch = NO;}}
短视频软件开发,仿抖音滑动播放视频的实现相关推荐
- android抖音切换实现,【Android 进阶】仿抖音系列之视频预览和录制(五)
前言 大家好,在前几篇中,我们通过2种方式实现了仿抖音的翻页切换视频,仿抖音列表播放视频功能:这一篇,我们来说说视频的录制. 主流的视频录制,一般都采用的是FFmpeg 例如 腾讯短视频,由于FFmp ...
- 如何开发仿抖音短视频APP源码?
如何开发仿抖音短视频APP源码? 流程列表 开发一个短视频最主要的流程分为 3 个,下面我将分步教你实现这 3 个流程下的各个功能点,功能点 API 可按需调用: 视频拍摄 a.启动拍摄 b.给拍摄添 ...
- 仿抖音滑动小短剧影视微信小程序源码带支付收益等模式
项目功能介绍:支持无限滑动 高性能滑动 预加载 视频预览 支持剧情介绍,集合壁纸另外仿抖音滑动效果 支持会员模式,支持用户单独购买等等多功能 丰富的后台设置,具体大家可以看小编的后台演示图 具体小编也 ...
- android抖音自动刷新,Android 使用SwipeRefreshLayout控件仿抖音做的视频下拉刷新效果...
SwipeRefreshLayout(这个控件),我先跟大家介绍一下这个控件: 一.SwipeRefreshLayout简单介绍 •先看以下官方文档,已有了很详细的描述了. 官方文档说明 •这里我再大 ...
- php+api抖音随机播放视频源码
简介: php+api抖音随机播放视频源码,前端纯静态html+css,数据采用php+API接口调用第三方数据,不需要配置环境,上传即用. 源码体积小,视频采集自网络,无视频资源,内置多条播放线路. ...
- 短视频源码仿抖音短视频APP源码短视频平台源码短视频源码
[WoShop仿抖音短视频源码的主要功能] 1.短视频带货:关联商品的短视频封面会有商品标识,短视频内容中会弹出商品链接 2.直播带货:短视频源码支持直播功能,直播间内可开启带货功能 3.邀请赚钱:用 ...
- flutter开发仿抖音首页面上下滑动切换播放视频效果
题记 -- 执剑天涯,从你的点滴积累开始,所及之处,必精益求精,即是折腾每一天. -----[视频教程 感兴趣的伙伴可以瞅瞅] 本小节讲述: 1 VideoPlayer 视频播放组件使用 2 Vide ...
- 微信小程序 短剧开发技术踩坑指南 仿抖音快手小视频
1.Video组件 微信官方文档地址: https://developers.weixin.qq.com/miniprogram/dev/component/video.html uniapp官方文档 ...
- 怎么让抖音视频当做铃声android,抖音怎么提取视频里的音乐?怎么将抖音里的音乐用来做手机铃声?抖音视频提取音频的方法...
虽然说抖音发展到现在,褒贬不一,但是架不住人家火啊,现在的人大都喜欢捧着个手机刷抖音,来打发一些碎片时光.抖音不仅带火了短视频,同样也带火了视频里面的背景音乐,许多我们平时并没有听过的歌曲在抖音上就慢 ...
最新文章
- 算力大战是BCH最坏的一种处理方式
- div地址跳转 vue_vue---导航栏点击跳转到对应位置
- 415. Add Strings
- 标线markLine的用法
- 钓鱼基础设施的应用分析(钓鱼邮件利用、Gophish)
- 评委输入的密码如果不正确,当错误的次数达到5次时,该评委的账号会被锁定.如何解锁呢?
- 清华大学829考研 初试436经验谈
- 显卡对决nbsp;NVIDIAnbsp;GTnbsp;555Mnbsp;VS…
- 老泪纵横!伴随数代人成长的中国经典动画
- php整数溢出 ctf,PWN INTEGER OVERFLOW 整数溢出
- errMsg: “getUserProfile:fail can only be invoked by user TAP gesture.
- MTI Further
- linux第一块ide硬盘命名为,linux下硬盘分区
- H5指北针JavaScript代码
- Linux 对整个系统备份和还原
- 【软件测试】——编写测试用例实例
- CANFiber是什么?
- https web service(转)
- H3C和华为配置端口聚合的问题
- 北京晚报:谷歌中国访问量陡增