iOS弹幕开发中遇到的问题汇总

前言

弹幕在现在的各类视频中都有,也是每位开发者们必须会的一个功能,最近在开发中就遇到了一些问题,下面简单说说弹幕开发碰到的两个小问题。

正文

  • 需求:实现一个弹幕容器,里面同时会有多行互不重叠的、运动中的弹幕 。每一条弹幕均需要支持点击事件。
  • 用脚底板想的方法:在弹幕容器里面创建几个 UIButton,并且 addTarget,增加点击事件。最后利用 UIView 的 block API 实现动画。
  • 结果:嗯...可惜的是,代码运行起来,你会发现在 UIButton 运动过程,点击事件并没有响应,而且非常奇怪的是:为什么在 UIButton 动画过程,去点击 UIButton 动画的终点,点击事件竟然响应了??这是为什么呢?
  • Core Anmation 动画过程原理的引用:

在iOS中,屏幕每秒钟重绘60次。如果动画时长比60分之一秒要长,Core Animation就需要在设置一次新值和新值生效之间,对屏幕上的图层进行重新组织。这意味着CALayer除了“真实”值(就是你设置的值)之外,必须要知道当前显示在屏幕上的属性值的记录。

每个图层属性的显示值都被存储在一个叫做呈现图层的独立图层当中,他可以通过-presentationLayer方法来访问。这个呈现图层实际上是模型图层的复制,但是它的属性值代表了在任何指定时刻当前外观效果。换句话说,你可以通过呈现图层的值来获取当前屏幕上真正显示出来的值。

补充:模型图层在动画开始的那一刻就已经达到终点位置,响应点击事件的也是它。

解决办法:

重写弹幕容器 view 的 touchesBegan 方法。代码如下:

@interface ZYYBarrageView ()
@property (nonatomic, strong) UIView *redView; // 将要做平移的 subview
@end
@implementation ZYYBarrageView
- (instancetype)initWithFrame:(CGRect)frame {
 self = [super initWithFrame:frame];
 if (self) {
  [self commonInit];
 }
 return self;
}
- (void)commonInit {
 self.redView = [[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, 30.f, 30.f)];
 self.redView.backgroundColor = [UIColor redColor];
 [self addSubview:self.redView];
}
- (void)touchesBegan:(NSSet<uitouch *> *)touches withEvent:(UIEvent *)event {
 // 重点开始!!UITouch 获取在 barrageView 坐标系下的坐标
 CGPoint touchPoint = [[touches anyObject] locationInView:self];
 // 判断触摸点是否在 redView 的呈现树的框框之中
 if ([self.redView.layer.presentationLayer hitTest:touchPoint]) {
  // 响应红色块点击
  return;
 } else {
 }
}</uitouch *>

进一步的需求:在 ZYYBarrageView 的同一层级,但层次偏后会有 UIButton。正常情况下,因为 ZYYBarrageView 的存在,UIButton 是无法响应点击事件的。代码如下:

@property (nonatomic, strong) ZYYBarrageView *barrageView; // 弹幕 view 支持多行 view 在里面进行运动
@property (nonatomic, strong) UIButton *yellowBtn; // 靠后的 UIButton
- (void)viewDidLoad {
 [super viewDidLoad];
 // self.yellowBtn 位于 self.barrageView 之后
 [self.view addSubview:self.yellowBtn];
 [self.view addSubview:self.barrageView];
}
- (ZYYBarrageView *)barrageView {
 if (!_barrageView) {
  _barrageView = [[ZYYBarrageView alloc] initWithFrame:CGRectMake(0.f, 30.f, SCREEN_WIDTH, 30.f)];
  _barrageView.backgroundColor = [UIColor clearColor];
 }
 return _barrageView;
}
- (UIButton *)yellowBtn {
 if (!_yellowBtn) {
  _yellowBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  _yellowBtn.frame = CGRectMake(90.f, 30.f, 80.f, 30.f);
  _yellowBtn.backgroundColor = [UIColor yellowColor];
  [_yellowBtn setTitle:@"黄色按钮" forState:UIControlStateNormal];
  [_yellowBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  [_yellowBtn addTarget:self action:@selector(onYellowBtn:) forControlEvents:UIControlEventTouchUpInside];
 }
 return _yellowBtn;
}
- (void)onYellowBtn:(id)sender {
 // 响应黄色按钮
}

怎么办?

Responder Chain 原理讲解:手指点击屏幕,经过系统响应(之前过程省略不说,文末有参考链接),调用 UIApplication 的 sendEvent: 方法,将 UIEvent 传给 UIWindow, 通过递归调用 UIView 层级的 hitTest(_:with:) ,结合 point(inside:with:) 找到 UIEvent 中每一个UITouch 所属的 UIView(其实是想找到离触摸事件点最近的那个 UIView)。这个过程是从 UIView 层级的最顶层往最底层递归查询。同一层级的 UIView,会优先深度遍历界面靠前的 UIView。找到最底层 UIView 后,沿着 Responder Chain 逐步向上传递(UIControl 子类默认会拦截传递)。

解决思路:重写 ZYYBarrageView 的 hitTest(_:with:) 方法。代码如下:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
 BOOL isPointInsideSubview = [self.redView.layer.presentationLayer hitTest:point];
 if (isPointInsideSubview == NO) {
  // 如果没有点击在移动的 redView 上,返回 nil
  // 系统会去遍历位于 ZYYBarrageView 后面的 UIButton,UIButton 能得到响应
  return nil;
 } else {
  return [super hitTest:point withEvent:event];
 }
}

如此,可以完美解决啦~

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • ios弹幕高效加载实现方式实例代码

    看直播的童鞋们应该会经常看到满屏幕的滚动弹幕,看到密密麻麻的弹幕第一印象就是怎么样高效加载来避免卡顿,弹幕组成部分包含用户头像.用户昵称.弹幕的内容.表情等,本文介绍的实现原理就是把这几部分绘制成一张图片,然后通过定时器移动弹幕图片,当图片不在屏幕范围内即销毁. 先看下效果 下面我会详细介绍下实现原理 1 .获取弹幕数据来源,因为我是模拟生成弹幕,弹幕的数据存放在工程里的plist文件中 emotions存放这条弹幕的表情,type表示是否是自己发的,text表示弹幕内容,userName表示用

  • IOS 实现简单的弹幕功能

    前言 简单实现弹幕功能,表跟我谈效率,但也有用队列控制同时弹的数量. 正文 代码实现: let DANMAKU_SPEED: CGFloat = 150 // 弹幕每秒移动速度 let DANMAKU_SPACE_TIME: NSTimeInterval = 1 // 弹幕之间的时间间隔 let DANMAKU_MAX_ROW = 3 // 最多同时弹幕行数 let danmakuFont = UIFont.systemFontOfSize(18) // 弹幕字体大小 var rowArray

  • 代码详解iOS视频直播弹幕功能

    本篇内容通过步骤详细给大家讲解了iOS视频直播弹幕的原理以及实现代码分析,以下就是全部内容: 1.弹幕的实现性分析 首先,从视觉上明确当前弹幕所具有的功能 从屏幕右侧滑入左侧,直至完全消失 不管是长的弹幕,还是短的弹幕,速度一致(可能有的需求是依据弹幕长度,调整速度) 有弹幕轨道,不是随机产生的弹幕 弹幕不会进行重叠 接下来从功能角度思考需要做什么 重用机制,类似tableView有一个重用池,每个弹幕就是一个cell,当有弹幕发送的时候,如果当前的重用池没有控件,则创建一个新的控件,如果重用池

  • iOS弹幕开发中遇到的问题汇总

    前言 弹幕在现在的各类视频中都有,也是每位开发者们必须会的一个功能,最近在开发中就遇到了一些问题,下面简单说说弹幕开发碰到的两个小问题. 正文 需求:实现一个弹幕容器,里面同时会有多行互不重叠的.运动中的弹幕 .每一条弹幕均需要支持点击事件. 用脚底板想的方法:在弹幕容器里面创建几个 UIButton,并且 addTarget,增加点击事件.最后利用 UIView 的 block API 实现动画. 结果:嗯...可惜的是,代码运行起来,你会发现在 UIButton 运动过程,点击事件并没有响应

  • iOS App开发中扩展RCLabel组件进行基于HTML的文本布局

    iOS系统是一个十分注重用户体验的系统,在iOS系统中,用户交互的方案也十分多,然而要在label中的某部分字体中添加交互行为确实不容易的,如果使用其他类似Button的控件来模拟,文字的排版又将是一个解决十分困难的问题.这个问题的由来是项目中的一个界面中有一些广告位标签,而这些广告位的标签却是嵌在文本中的,当用户点击文字标签的位置时,会跳转到响应的广告页. CoreText框架和一些第三方库可以解决这个问题,但直接使用CoreText十分复杂,第三方库多注重于富文本的排版,对类似文字超链接的支

  • iOS应用开发中SQLite的初步配置指南

    iOS开发数据库篇-SQLite简单介绍 一.离线缓存 在项目开发中,通常都需要对数据进行离线缓存的处理,如新闻数据的离线缓存等. 说明:离线缓存一般都是把数据保存到项目的沙盒中.有以下几种方式 (1)归档:NSCodeing.NSKeyedArchiver (2)偏好设置:NSUserDefaults (3)Plist存储:writeToFile 提示:上述三种方法都有一个致命的缺点,那就是都无法存储大批量的数据,有性能的问题. 举例:使用归档 两个问题: (1)数据的存取都必须是完整的,要求

  • iOS应用开发中使UITextField实现placeholder属性的方法

    我们都知道iOS开发中的UITextField有个placeholder属性,placeholder可以很方便引导用户输入.但是UITextView却没有placeholder属性. 一.猥琐的方法 如何让UITextView也有placeholder功能呢?今天给各位分享一个比较猥琐的做法.思路大概是这样的: 把UITextView的text当placeholder使用. 在开始编辑的代理方法里清除placeholder. 在结束编辑的代理方法里在设置placeholder. 实现方法: 1.

  • iOS App开发中UIViewController类的使用教程

    一.引言 作为MVC设计模式中的C,Controller一直扮演着项目开发中最重要的角色,它是视图和数据的桥梁,通过它的管理,将数据有条有理的展示在我们的View层上.iOS中的UIViewController是UIKit框架中最基本的一个类.从第一个UI视图到复杂完整项目,都离不开UIViewController作为基础.基于UIViewController的封装和扩展,也能够出色的完成各种复杂界面逻辑.这里旨在讨论UIViewController的生命周期和属性方法,在最基础的东西上,往往会

  • iOS应用开发中使用Auto Layout来适配不同屏幕尺寸

    简介 Auto Layout 是苹果在 Xcode 5 (iOS 6) 中新引入的布局方式,旨在解决 3.5 寸和 4 寸屏幕的适配问题.屏幕适配工作在 iPhone 6 及 plus 发布以后变得更加重要,而且以往的"笨办法"的工作量大幅增加,所以很多人开始学习使用 Auto Layout 技术. 初体验 0. 开发环境 本系列文章的开发环境为: OS X 10.10.3 Xcode Version 6.3.1 (6D1002) 1. 新建应用 新建一个 Single View Ap

  • iOS应用开发中视图控件UIWindow的基本使用教程

    一.简单介绍 iPhone应用程序通常只有一个窗口,表示为一个UIWindow类的实例.应用程序在启动时(或者从nib文件进行装载)创建这个窗口,并往窗口中加入一或多个视图并显示出来.之后我们很少需要再次引用它.UIWindow对象是所有UIView的根,管理和协调的应用程序的显示.一般应用程序只有一个UIWindow对象,即使有多个UIWindow对象,也只有一个UIWindow可以接受到用户的触屏事件. 在IOS中,UIWindow对象并没有像windows应用程序中常见的关闭框或标题栏这样

  • iOS App开发中使cell高度自适应的黑魔法详解

    在使用 table view 的时侯经常会遇到这样的需求:table view 的 cell 中的内容是动态的,导致在开发的时候不知道一个 cell 的高度具体是多少,所以需要提供一个计算 cell 高度的算法,在每次加载到这个 cell 的时候计算出 cell 真正的高度. 在 iOS 8 之前 没有使用 Autolayout 的情况下,需要实现 table view delegate 的 tableView(tableView: UITableView, heightForRowAtInde

  • iOS App开发中Core Data框架基本的数据管理功能小结

    一.何为CoreData CoreData是一个专门用来管理数据的框架,其在性能与书写方便上都有很大的优势,在数据库管理方面,apple强烈推荐开发者使用CoreData框架,在apple的官方文档中称,使用CoreData框架可以减少开发者50%--70%的代码量,这虽然有些夸张,但由此可见,CoreData的确十分强大. 二.设计数据模型 在iOS开发中,时常使用SQL数据库对大量的表结构数据进行处理,但是SQL有一个十分明显的缺陷,对于常规数据模型的表,其处理起来是没问题的,例如一个班级表

  • iOS应用开发中使用设计模式中的观察者模式的实例

    在软件开发中,无论是那种高级语言中总会伴随着一些最为常用的设计模式,即便就如iOS开发中与我们打交道最多的无非就是单例模式.观察者模式和工厂模式了,当然了其他的设置模式也同样存在在编程的很多地方.下面就就让我们简单的了解下观察者模式吧! 观察者模式本质上时一种发布-订阅模型,用以消除具有不同行为的对象之间的耦合,通过这一模式,不同对象可以协同工作,同时它们也可以被复用于其他地方Observer从Subject订阅通知,ConcreteObserver实现重现ObServer并将其重载其updat

随机推荐