重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
小编给大家分享一下iOS弹幕开发中遇到的问题有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
10年积累的网站设计、做网站经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站设计后付款的网站建设流程,更有鄂伦春免费网站建设让你可以放心的选择与我们合作。正文
需求:实现一个弹幕容器,里面同时会有多行互不重叠的、运动中的弹幕 。每一条弹幕均需要支持点击事件。
用脚底板想的方法:在弹幕容器里面创建几个 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*)touches withEvent:(UIEvent *)event { // 重点开始!!UITouch 获取在 barrageView 坐标系下的坐标 CGPoint touchPoint = [[touches anyObject] locationInView:self]; // 判断触摸点是否在 redView 的呈现树的框框之中 if ([self.redView.layer.presentationLayer hitTest:touchPoint]) { // 响应红色块点击 return; } else { } }
进一步的需求:在 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]; } }
以上是“iOS弹幕开发中遇到的问题有哪些”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联网站建设公司行业资讯频道!
另外有需要云服务器可以了解下创新互联建站www.cdcxhl.com,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。