如何在UITableViewCell中正确展示AVPlayerViewController?
解决UITableViewCell中AVPlayerViewController点击触发cell选中的问题
这个问题我之前做视频列表业务时也碰到过,本质是触摸事件穿透了AVPlayerViewController的视图,传递到下方的UITableViewCell,从而触发了tableView:didSelectRowAtIndexPath:回调。下面给你几个实用的解决思路:
方法一:直接禁用Cell的选中状态(适合不需要选中效果的场景)
如果你的Cell本来就不需要点击选中的交互,最简单的办法就是禁用它的选中样式:
// 可以在自定义Cell的awakeFromNib或者初始化方法里设置 self.selectionStyle = UITableViewCellSelectionStyleNone; // 或者在tableView:cellForRowAtIndexPath:中配置Cell时设置 cell.selectionStyle = UITableViewCellSelectionStyleNone;
这种方式零成本,但如果你的Cell还有其他需要选中的业务场景,就不太适用了。
方法二:让AVPlayerViewController的视图拦截触摸事件
这是更灵活的方案,核心是确保播放器的视图能正确承接触摸事件,不让事件往下透传给Cell。
子方案1:给播放器视图添加透明遮罩(快速实现)
在你展示播放器的方法里,给AVPlayerViewController的view加一个覆盖整个区域的透明UIView,让它来接收触摸事件:
-(void)showVideoInView:(UIView *)view URL:(NSURL *)url { if (!url || !view) { return; } AVPlayerViewController *playerVC = [[AVPlayerViewController alloc] init]; playerVC.player = [AVPlayer playerWithURL:url]; playerVC.view.frame = CGRectIntegral(view.bounds); // 添加透明遮罩,拦截触摸事件 UIView *touchMask = [[UIView alloc] initWithFrame:playerVC.view.bounds]; touchMask.userInteractionEnabled = YES; touchMask.backgroundColor = [UIColor clearColor]; // 注意要把遮罩放在播放器控制条的下层,避免挡住控制交互 [playerVC.view insertSubview:touchMask atIndex:0]; [self addChildViewController:playerVC]; [view addSubview:playerVC.view]; [playerVC didMoveToParentViewController:self]; [playerVC.player play]; }
这里要注意遮罩的层级,插在最底层就不会影响播放器控制条的交互,同时能拦截透传的触摸事件。
子方案2:自定义AVPlayerViewController子类(更优雅)
创建一个AVPlayerViewController的子类,重写它的view的hitTest:withEvent:方法,确保触摸事件被正确拦截:
@interface CustomPlayerVC : AVPlayerViewController @end @implementation CustomPlayerVC - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *hitView = [super hitTest:point withEvent:event]; // 如果触摸点不在播放器的控制元素上,就让当前view承接事件,阻止透传 if (!hitView || hitView == self.view) { return self.view; } // 控制元素的触摸正常响应 return hitView; } @end
然后在你的展示方法里替换成这个子类:
-(void)showVideoInView:(UIView *)view URL:(NSURL *)url { if (!url || !view) { return; } CustomPlayerVC *playerVC = [[CustomPlayerVC alloc] init]; playerVC.player = [AVPlayer playerWithURL:url]; playerVC.view.frame = CGRectIntegral(view.bounds); [self addChildViewController:playerVC]; [view addSubview:playerVC.view]; [playerVC didMoveToParentViewController:self]; [playerVC.player play]; }
这种方式既不会影响播放器控制条的正常交互,又能彻底阻止事件透传到Cell,是我最推荐的方案。
方法三:在didSelectRow里判断点击区域(兜底方案)
如果上面的方法都不适合你的场景,还可以在tableView:didSelectRowAtIndexPath:里判断点击的是不是播放器区域,是的话就跳过选中逻辑:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; // 假设你的播放器容器视图是cell的某个子视图,比如cell.playerContainerView UIView *playerContainer = cell.playerContainerView; CGPoint touchPoint = [tableView convertPoint:[[tableView touchesForEvent:nil] anyObject].locationInView:tableView toView:playerContainer]; if (CGRectContainsPoint(playerContainer.bounds, touchPoint)) { // 点击的是播放器区域,取消选中并返回 [tableView deselectRowAtIndexPath:indexPath animated:YES]; return; } // 这里处理正常的Cell选中逻辑 }
不过这种方式需要处理触摸点的转换,相对麻烦一些,适合临时兜底用。
内容的提问来源于stack exchange,提问作者BaQiWL
相关产品推荐
相关产品推荐

