ZFPlayer 在tableView列表中播放视频架构设计
需求背景
需要在如图所示的列表中播放视频,并且播放视频在对应的卡片上,滚动结束的时候, 完整露出封面图的第一个视频自动播放
分析
根据需求,是滚动的时候获取符合条件的cell,并且
在cell的封面图上播放视频,从上往下,第一个完全展示的
cell播放视频
这就要求,我们的播放器应该是页面级别的,而不应该是cell 级别的,并且ZFPlayer框架支持这种自动播放的需求,
但是和我们的有些不同,我们这个列表是多种cell同时存在的,但是只有一种cell支持自动播放,所以要求我们进行
定制化开发
获取当前适合条件的cell
其实zfplayer是有获取当前符合条件的cell的方法的,但是
和我们的判断条件不一样,并且滚动回调是在这里设置的
如图,这里要我们ZFPlayer必须存在,但是我们页面消失之后,回
销毁ZFplayer,如果再回到这个页面,再滚动页面的时候,就没有这个
回调了,所以,我们直接在页面的scrollview代理方法中调用
如下代码
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[super scrollViewDidEndDecelerating:scrollView];
[self findCorrectCell];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
[super scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
if (!decelerate) {
[self findCorrectCell];
}
}
- (void)findCorrectCell
{
if (self.channelBannerTool.playerView.currentPlayerManager.isPlaying) {
//如果正在播放广告, 不自动播放
return;
}
NSArray *cells = self.tableView.visibleCells;
CGFloat scrollViewMidY = CGRectGetHeight(self.tableView.frame)/2;
/// The final playing indexPath.
__block NSIndexPath *finalIndexPath = nil;
@weakify(self)
[cells enumerateObjectsUsingBlock:^(UITableViewCell *cell, NSUInteger idx, BOOL * _Nonnull stop) {
@strongify(self)
if (![cell conformsToProtocol:@protocol(ZFPlayerCellAutoPlayProtocol)] ||
![cell respondsToSelector:@selector(zf_cellShouldAutoplay)] ||
![(id<ZFPlayerCellAutoPlayProtocol>)cell zf_cellShouldAutoplay]) {
//如果不支持自动播放,则返回
return ;
}
UIView *playerView = [cell viewWithTag:AxCoverImageViewTag];
if (!playerView) return;
CGRect rect1 = [playerView convertRect:playerView.frame toView:self.tableView];
CGRect rect = [self.tableView convertRect:rect1 toView:self.tableView.superview];
/// playerView top to scrollView top space.
CGFloat topSpacing = CGRectGetMinY(rect) - CGRectGetMinY(self.tableView.frame) - CGRectGetMinY(playerView.frame);
/// playerView bottom to scrollView bottom space.
CGFloat bottomSpacing = CGRectGetMaxY(self.tableView.frame) - CGRectGetMaxY(rect) + CGRectGetMinY(playerView.frame);
CGFloat centerSpacing = ABS(scrollViewMidY - CGRectGetMidY(rect));
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
/// Play when the video playback section is visible.
if ((topSpacing - self.autoplaytopCover >= 0) && (bottomSpacing - bottomBarHeight >= 0)) {
/// If you have a cell that is playing, stop the traversal.
if (!finalIndexPath) {
/*
!self.playerView.playingIndexPath 说明当前没有正在播放的视频,
这个时候是要播放视频的,
self.playerView.playingIndexPath.row > indexPath.row 说明当前播放的
虽然现在有视频正在播放,但并不是第一个适合播放的视频,
所以这时候需要取第一条适合播放的视频播放
*/
if ((!self.playerView.playingIndexPath || self.playerView.playingIndexPath.row > indexPath.row)
&& [cell isKindOfClass:[PaperChannelVideoBaseCell class]]) {
listContObjectVO *listBO = self.dataList[indexPath.row];
if (![listBO isKindOfClass:[listContObjectVO class]]) {
return;
}
NSString *url =!isBlankString(listBO.videos[@"hdurl"])?listBO.videos[@"hdurl"]:listBO.videos[@"url"];
;
BOOL isLiving = [self isLivingTypeBOWithChannelListBO:listBO];
if (isLiving) {
NSDictionary *videoDic = listBO.liveInfo.videoLivingRoomSrcs[0];
url = videoDic[@"videoUrl"];
}
if (isBlankString(url)) {
return;
}
finalIndexPath = indexPath;
PaperChannelVideoBaseCell *cell = (PaperChannelVideoBaseCell *) [self.tableView cellForRowAtIndexPath:indexPath];
[self playWithListBO:listBO cell:cell isClick:NO];
}
}
}
}];
}
cell自动停止播放逻辑
有自动开始播放,就有自动停止播放,我们要求是一个封面图顶部刚被盖住的时候,就停止播放,即如果封面无法完全展示,就停止播放
ZFPlayer是自带停止播放的功能的,
根据以上代码,我们可以看出,需要我们设置,遮盖的距离和消失的比例
我们设置如下
//注意,距离是设置给tableView, 消失比例设置给ZFPlayerController
self.tableView.videoScrollBottomDisapperPadding = bottomBarHeight;
self.tableView.videoScrollTopDisapperPadding = SafeAreaTopHeight + 44*PLUS_SCALE;
self.playerController.playerDisapperaPercent = 0.0000001;
创建PlayerController
if (!self.playerView) {
ZFAVPlayerManager *playmanger = [[ZFAVPlayerManager alloc]init];
self.playerView = [[ZFPlayerController alloc]initWithScrollView:self.tableView playerManager:playmanger containerViewTag:AxCoverImageViewTag];
self.playerView.stopWhileNotVisible = self.showSmallFloatView;
self.playerView.controlView = self.controlView;
self.playerView.isInlistCell = YES;
self.playerView.onlySupportFullScreenForPortraitVideo = YES;
self.playerView.canDownDragToScaleToExitFullScreen = YES;
self.playerView.smallFloatView.fatherView = self.view;
self.playerView.smallFloatView.toppadding = self.tableView.videoScrollTopDisapperPadding;
self.playerView.playerApperaPercent = 0.3;
self.playerView.playerDisapperaPercent = 1;
self.playerView.analysisModel = videoBO.bigData_AnalysisModel;
// config 竖视频的东西
self.controlView.disableHorizontalPanWhenSpecialPortraitVideo = YES;
///湃客视频不显示画中画
if ([videoBO.forwordType intValue]==MediaNumberVideoNewsForwardType||
[videoBO.forwordType intValue]==PaiGuestVideoNewsForwardType) {
self.controlView.portraitControlView.isCanShowButton = NO;
}else{
self.controlView.portraitControlView.isCanShowButton = YES;
///画中画-与广告角标冲突
self.controlView.controlViewAppearedCallback = ^(BOOL appeared) {
if (appeared) {
cell.cornerDesc.alpha = 0;
}else{
cell.cornerDesc.alpha = 1;
}
};
}
WEAKSELF
self.landScapeControlView.shareButtonAction = ^{
[weakSelf shareHanderWith:videoBO withBtn:[UIButton new]];
};
self.landScapeControlView.pushToDetailVideoContent = ^(listContObjectVO * _Nonnull channelBO) {
[weakSelf channelPsuhWithListBO:channelBO indexpath:channelBO.indexPath];
};
// 防止瀑布流滑动的时候会触发声音或者亮度的滑动时间
self.playerView.disablePanMovingDirection = ZFPlayerDisablePanMovingDirectionVertical;
// WEAKSELF
self.playerView.cancleActionWhen4Gplay = ^{
[weakSelf resetPlayerView];
};
self.playerView.smallFloatView.closeClickCallback = ^{
[weakSelf resetPlayerView];
};
self.playerView.playerDidToEnd = ^(id<ZFPlayerMediaPlayback> _Nonnull asset) {
[weakSelf resetPlayerView];
};
self.controlView.playerDidToStopPlayVideo = ^{
[weakSelf resetPlayerView];
};
self.playerView.currentPlayerManager.muted = ![TPUserDefault instance].openChannelAutoPlayVoice;
self.playerView.playerApperaPercent = 1;
self.playerView.playerDisapperaPercent = 0.0000001;
self.playerView.onlySupportFullScreenForPortraitVideo = NO;
if (self.playerView.gestureControl.panGR && [self.playerView.currentPlayerManager.view.gestureRecognizers containsObject:self.playerView.gestureControl.panGR]) {
[self.playerView.currentPlayerManager.view removeGestureRecognizer:self.playerView.gestureControl.panGR];
}
WEAKSELF
self.playerView.playerPlayTimeChanged = ^(id<ZFPlayerMediaPlayback> _Nonnull asset, NSTimeInterval currentTime, NSTimeInterval duration) {
weakSelf.currentTime = currentTime;
if (currentTime >= duration) {
weakSelf.currentTime = 0;
}
};