新增 Banner 手势优化实施总结文档,记录了在 RoomAnimationView 中对 banner 手势系统的优化过程,包括手势识别器的重新设计、区域划分逻辑、tap 手势处理逻辑及交互区域检查等。同时,优化了多个视图中的通知处理逻辑,确保手势事件的准确传递与处理,提升用户交互体验。

This commit is contained in:
edwinQQQ
2025-08-15 19:34:25 +08:00
parent 84e146277a
commit 3f97b0293e
11 changed files with 871 additions and 910 deletions

View File

@@ -0,0 +1,248 @@
# Banner手势优化实施总结
## 概述
本文档记录了在 `RoomAnimationView.m` 中对 banner 手势系统的优化实施过程。
## 最新优化内容2025年1月
### 需求描述
1. **bannerContainer 手势范围调整**
- 中央宽度 2/3 的位置:保留 swipe 手势
- 左右两侧各 1/6 宽度:添加 tap 手势
2. **tap 手势处理逻辑**
- 检查当前显示的 banner 是否在 tap 位置可以响应事件
- 如果可以响应:不处理,让 banner 继续原有逻辑
- 如果不能响应:保存 tap 位置点,供后续使用
### 实施方案
#### 1. 手势识别器重新设计
```objc
- (void)addBnnerContainGesture {
// 创建独立的手势容器避免与XPRoomAnimationHitView的hitTest冲突
[self insertSubview:self.bannerSwipeGestureContainer aboveSubview:self.bannerContainer];
[self insertSubview:self.bannerLeftTapGestureContainer aboveSubview:self.bannerContainer];
[self insertSubview:self.bannerRightTapGestureContainer aboveSubview:self.bannerContainer];
// 设置手势容器的布局约束
[self.bannerSwipeGestureContainer mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.mas_equalTo(self.bannerContainer);
make.top.bottom.mas_equalTo(self.bannerContainer);
make.width.mas_equalTo(self.bannerContainer.mas_width).multipliedBy(2.0/3.0);
}];
[self.bannerLeftTapGestureContainer mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.leading.bottom.mas_equalTo(self.bannerContainer);
make.trailing.mas_equalTo(self.bannerSwipeGestureContainer.mas_leading);
}];
[self.bannerRightTapGestureContainer mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.trailing.bottom.mas_equalTo(self.bannerContainer);
make.leading.mas_equalTo(self.bannerSwipeGestureContainer.mas_trailing);
}];
// 创建中央区域的 swipe 手势2/3 宽度)
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:@selector(handleSwipe)];
if (isMSRTL()) {
swipe.direction = UISwipeGestureRecognizerDirectionRight;
} else {
swipe.direction = UISwipeGestureRecognizerDirectionLeft;
}
swipe.delegate = self;
// 创建左侧区域的 tap 手势1/6 宽度)
UITapGestureRecognizer *leftTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleBannerTap:)];
leftTap.delegate = self;
// 创建右侧区域的 tap 手势1/6 宽度)
UITapGestureRecognizer *rightTap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleBannerTap:)];
rightTap.delegate = self;
// 添加手势识别器到对应的手势容器
[self.bannerSwipeGestureContainer addGestureRecognizer:swipe];
[self.bannerLeftTapGestureContainer addGestureRecognizer:leftTap];
[self.bannerRightTapGestureContainer addGestureRecognizer:rightTap];
}
```
#### 2. 区域划分逻辑
```objc
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
CGPoint touchPoint = [touch locationInView:self.bannerContainer];
CGFloat containerWidth = self.bannerContainer.bounds.size.width;
// 计算区域边界
CGFloat leftBoundary = containerWidth / 6.0; // 1/6 宽度
CGFloat rightBoundary = containerWidth * 5.0 / 6.0; // 5/6 宽度
if ([gestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]]) {
// Swipe 手势只在中央 2/3 区域生效
return touchPoint.x >= leftBoundary && touchPoint.x <= rightBoundary;
} else if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
// Tap 手势只在左右两侧 1/6 区域生效
return touchPoint.x < leftBoundary || touchPoint.x > rightBoundary;
}
return YES;
}
```
#### 3. Tap 手势处理逻辑
```objc
- (void)handleBannerTap:(UITapGestureRecognizer *)tapGesture {
CGPoint tapPoint = [tapGesture locationInView:self.bannerContainer];
// 检查当前显示的 banner 是否在 tap 位置可以响应事件
if ([self isPointInBannerInteractiveArea:tapPoint]) {
// banner 可以响应,不处理,让 banner 继续原有逻辑
NSLog(@"🎯 Banner tap 位置在可交互区域banner 将处理此事件");
return;
} else {
// banner 不能响应,保存 tap 位置
self.savedTapPoint = tapPoint;
self.hasSavedTapPoint = YES;
NSLog(@"💾 Banner tap 位置不在可交互区域,已保存位置: %@", NSStringFromCGPoint(tapPoint));
}
}
```
#### 4. Banner 交互区域检查
```objc
- (BOOL)isPointInBannerInteractiveArea:(CGPoint)point {
// 检查当前显示的 banner 是否在指定位置可以响应事件
for (UIView *subview in self.bannerContainer.subviews) {
if (subview.hidden || subview.alpha <= 0.01) {
continue;
}
// 检查点是否在子视图范围内
if (CGRectContainsPoint(subview.bounds, point)) {
// 检查子视图是否支持用户交互
if (subview.userInteractionEnabled) {
// 进一步检查子视图是否有可点击的元素
CGPoint subviewPoint = [subview convertPoint:point fromView:self.bannerContainer];
UIView *hitView = [subview hitTest:subviewPoint withEvent:nil];
if (hitView && hitView.userInteractionEnabled) {
return YES;
}
}
}
}
return NO;
}
```
#### 5. 公共接口方法
```objc
// 获取保存的 tap 位置
- (CGPoint)getSavedTapPoint;
// 检查是否有保存的 tap 位置
- (BOOL)hasSavedTapPointAvailable;
// 清除保存的 tap 位置
- (void)clearSavedTapPoint;
```
### 新增属性
```objc
// Banner 手势相关属性
@property(nonatomic, assign) CGPoint savedTapPoint;
@property(nonatomic, assign) BOOL hasSavedTapPoint;
// 手势容器使用普通UIView避免XPRoomAnimationHitView的hitTest冲突
@property(nonatomic, strong) UIView *bannerSwipeGestureContainer;
@property(nonatomic, strong) UIView *bannerLeftTapGestureContainer;
@property(nonatomic, strong) UIView *bannerRightTapGestureContainer;
```
### 协议支持
- 添加了 `UIGestureRecognizerDelegate` 协议支持
- 实现了手势识别器的 delegate 方法
## 技术特点
### 1. 精确的区域控制
- 使用独立的手势容器精确划分区域
- 中央 2/3 区域swipe 手势容器
- 左右两侧各 1/6 区域tap 手势容器
### 2. 避免手势冲突
- 使用普通 `UIView` 作为手势容器,避免 `XPRoomAnimationHitView``hitTest` 冲突
- 手势容器独立于 banner 内容,确保手势识别不受干扰
### 3. 智能的事件处理
- 检查 banner 是否在 tap 位置可响应
- 自动判断是否需要保存 tap 位置
- 避免与 banner 原有交互逻辑冲突
### 4. 灵活的接口设计
- 提供公共方法获取保存的 tap 位置
- 支持清除保存的位置
- 便于外部代码使用
### 5. 完善的日志记录
- 详细记录手势处理过程
- 便于调试和问题排查
## 使用示例
```objc
// 检查是否有保存的 tap 位置
if ([roomAnimationView hasSavedTapPointAvailable]) {
CGPoint savedPoint = [roomAnimationView getSavedTapPoint];
NSLog(@"保存的 tap 位置: %@", NSStringFromCGPoint(savedPoint));
// 使用保存的位置进行后续处理
// ...
// 清除保存的位置
[roomAnimationView clearSavedTapPoint];
}
```
## 注意事项
1. **手势容器设计**:使用普通 `UIView` 作为手势容器,避免 `XPRoomAnimationHitView``hitTest` 冲突
2. **区域划分**:通过独立的视图容器精确划分手势区域,确保手势识别不受干扰
3. **交互检查**:通过 `hitTest` 方法检查子视图的实际可交互性
4. **内存管理**:及时清除不需要的 tap 位置数据
5. **调试支持**:在 DEBUG 模式下为手势容器添加背景色,便于调试区域划分
## 测试建议
1. **区域划分测试**
- 在中央区域测试 swipe 手势
- 在左右两侧测试 tap 手势
- 验证手势在错误区域不触发
2. **交互逻辑测试**
- 在有可交互 banner 的区域 tap
- 在无可交互 banner 的区域 tap
- 验证 tap 位置的保存和清除
3. **边界条件测试**
- 测试不同屏幕尺寸下的区域划分
- 测试 RTL 语言环境下的手势方向
- 测试多个 banner 同时显示的情况
## 总结
本次优化成功实现了:
- ✅ bannerContainer 手势范围的精确划分
- ✅ 智能的 tap 手势处理逻辑
- ✅ 灵活的 tap 位置保存机制
- ✅ 完善的公共接口设计
- ✅ 与现有代码的良好兼容性
- ✅ 解决了 XPRoomAnimationHitView 的手势冲突问题
### 关键改进
1. **避免手势冲突**:使用普通 `UIView` 作为手势容器,避免 `XPRoomAnimationHitView``hitTest` 方法干扰
2. **精确区域控制**:通过独立的视图容器实现精确的手势区域划分
3. **调试友好**:在 DEBUG 模式下为手势容器添加背景色,便于调试
该方案既满足了新的功能需求,又解决了潜在的手势冲突问题,保持了代码的可维护性和扩展性。

View File

@@ -90,24 +90,24 @@
CGFloat width = KScreenWidth; CGFloat width = KScreenWidth;
CGFloat height = kGetScaleWidth(145); CGFloat height = kGetScaleWidth(145);
CGFloat topSpace = kGetScaleWidth(67); // CGFloat topSpace = kGetScaleWidth(67);
CPGiftBanner *bannerView = [[CPGiftBanner alloc] initWithFrame:CGRectMake(KScreenWidth, 0, width, height)]; CPGiftBanner *bannerView = [[CPGiftBanner alloc] initWithFrame:CGRectMake(KScreenWidth, 0, width, height)];
bannerView.bannerAttachment = attachment; bannerView.bannerAttachment = attachment;
bannerView.completeDisplay = complete; bannerView.completeDisplay = complete;
[superView addSubview:bannerView]; [superView addSubview:bannerView];
[bannerView addNotification];
@kWeakify(bannerView); @kWeakify(bannerView);
// 使 POP banner // 使 POP banner
[bannerView popEnterAnimation:^(BOOL finished) { [bannerView popEnterAnimation:^(BOOL finished) {
if (finished) { if (finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@kStrongify(bannerView); @kStrongify(bannerView);
[bannerView addNotification];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[bannerView popLeaveAnimation:^(bool finished) { [bannerView popLeaveAnimation:^(bool finished) {
if (bannerView.completeDisplay) { if (bannerView.completeDisplay) {
bannerView.completeDisplay(); bannerView.completeDisplay();
} }
[bannerView removeNotification];
[bannerView removeFromSuperview]; [bannerView removeFromSuperview];
}]; }];
}); });
@@ -115,15 +115,38 @@
}]; }];
} }
- (void)addNotification { - (void)handleTapNotification:(NSNotification *)note {
@kWeakify(self); NSValue *value = note.userInfo[@"point"];
[[NSNotificationCenter defaultCenter] addObserverForName:@"SwipeOutBanner" CGPoint point = [value CGPointValue];
// banner
CGPoint screenPoint = [self convertPoint:point toView:nil];
// FunctionContainer
[[NSNotificationCenter defaultCenter] postNotificationName:@"BannerTapToFunctionContainer"
object:nil object:nil
queue:[NSOperationQueue mainQueue] userInfo:@{@"point": [NSValue valueWithCGPoint:screenPoint]}];
usingBlock:^(NSNotification * _Nonnull notification) { }
@kStrongify(self);
- (void)handleSwipeNotification {
[self dismissBanner]; [self dismissBanner];
}]; }
- (void)addNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleSwipeNotification)
name:@"SwipeOutBanner"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleTapNotification:)
name:@"TapBanner"
object:nil];
}
- (void)removeNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self];
} }
- (void)dismissBanner { - (void)dismissBanner {

View File

@@ -13,6 +13,7 @@
#import <JXCategoryView/JXCategoryView.h> #import <JXCategoryView/JXCategoryView.h>
#import <MJRefresh/MJRefresh.h> #import <MJRefresh/MJRefresh.h>
#import "SDWebImageManager.h" #import "SDWebImageManager.h"
#import <UIKit/UIKit.h>
///Tool ///Tool
#import "Api+Home.h" #import "Api+Home.h"
#import "YUMIMacroUitls.h" #import "YUMIMacroUitls.h"
@@ -24,6 +25,7 @@
#import "TTPopup.h" #import "TTPopup.h"
#import "NSArray+Safe.h" #import "NSArray+Safe.h"
#import "XPWeakTimer.h" #import "XPWeakTimer.h"
#import "XPRoomAnimationHitView.h"
///Model ///Model
#import "HomeTagModel.h" #import "HomeTagModel.h"
#import "AccountModel.h" #import "AccountModel.h"
@@ -663,21 +665,34 @@ XPHomeRecommendOtherRoomViewDelegate>
#pragma mark - JXPagerMainTableViewGestureDelegate #pragma mark - JXPagerMainTableViewGestureDelegate
- (BOOL)mainTableViewGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { - (BOOL)mainTableViewGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
//categoryView //
// 1. categoryView
if (otherGestureRecognizer.view == self.pagingView.listContainerView) { if (otherGestureRecognizer.view == self.pagingView.listContainerView) {
return NO; return NO;
} }
// 2. tag
if(otherGestureRecognizer.view.tag == 9000001){ if(otherGestureRecognizer.view.tag == 9000001){
return NO; return NO;
} }
if(otherGestureRecognizer.view.tag == 9000002){ if(otherGestureRecognizer.view.tag == 9000002){
return NO; return NO;
} }
if(otherGestureRecognizer.view.tag == 98777){ if(otherGestureRecognizer.view.tag == 98777){
return NO; return NO;
} }
// // 3. RoomAnimationViewbanner
// // otherGestureRecognizerRoomAnimationViewbannerContainer
// if ([otherGestureRecognizer.view isKindOfClass:[XPRoomAnimationHitView class]] &&
// [NSStringFromClass([otherGestureRecognizer.view class]) containsString:@"XPRoomAnimationHitView"]) {
// // bannerContainer
// NSLog(@"🎯 JXPagerView与Banner手势冲突处理: 允许同时识别");
// return YES;
// }
// 4. Pan
return [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]; return [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]];
} }
#pragma mark - XPHomeContainerProtocol #pragma mark - XPHomeContainerProtocol

View File

@@ -47,17 +47,17 @@
bannerView.gameID = model.skipContent; bannerView.gameID = model.skipContent;
[superView addSubview:bannerView]; [superView addSubview:bannerView];
[bannerView addNotification];
@kWeakify(bannerView); @kWeakify(bannerView);
[bannerView popEnterAnimation:^(BOOL finished) { [bannerView popEnterAnimation:^(BOOL finished) {
@kStrongify(bannerView);
[bannerView addNotification];
if (finished && bannerView.alreadyCancel == NO) { if (finished && bannerView.alreadyCancel == NO) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@kStrongify(bannerView);
[bannerView popLeaveAnimation:^(bool finished) { [bannerView popLeaveAnimation:^(bool finished) {
if (bannerView.completeDisplay) { if (bannerView.completeDisplay) {
bannerView.completeDisplay(); bannerView.completeDisplay();
} }
[bannerView removeNotification];
[bannerView removeFromSuperview]; [bannerView removeFromSuperview];
}]; }];
}); });
@@ -65,15 +65,49 @@
}]; }];
} }
- (void)addNotification { - (void)handleSwipeNotification {
@kWeakify(self);
[[NSNotificationCenter defaultCenter] addObserverForName:@"SwipeOutBanner"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * _Nonnull notification) {
@kStrongify(self);
[self dismissBanner]; [self dismissBanner];
}]; }
- (void)handleTapNotification:(NSNotification *)note {
NSValue *value = note.userInfo[@"point"];
CGPoint point = [value CGPointValue];
NSLog(@"🔄 GameUniversalBannerView: 接收到点击点 %@ (bannerContainer坐标系)", NSStringFromCGPoint(point));
// bannerContainer GameUniversalBannerView
CGPoint bannerPoint = [self convertPoint:point fromView:self.superview];
NSLog(@"🔄 GameUniversalBannerView: 转换为 banner 坐标系 %@", NSStringFromCGPoint(bannerPoint));
NSLog(@"%@", CGRectContainsPoint(self.goButton.frame, bannerPoint) ? @"YES" : @"NO");
// go
CGPoint goButtonPoint = [self.goButton convertPoint:bannerPoint fromView:self];
if ([self.goButton pointInside:goButtonPoint withEvent:nil]) {
NSLog(@"🎯 GameUniversalBannerView: tap 点与 go 按钮重合,触发游戏跳转事件");
[self handleTapGo];
} else {
CGPoint screenPoint = [self convertPoint:point toView:nil];
// FunctionContainer
[[NSNotificationCenter defaultCenter] postNotificationName:@"BannerTapToFunctionContainer"
object:nil
userInfo:@{@"point": [NSValue valueWithCGPoint:screenPoint]}];
}
}
- (void)addNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleSwipeNotification)
name:@"SwipeOutBanner"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleTapNotification:)
name:@"TapBanner"
object:nil];
}
- (void)removeNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self];
} }
- (void)dismissBanner { - (void)dismissBanner {
@@ -358,16 +392,16 @@
} }
// 穿 // 穿
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { //- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.userInteractionEnabled || self.hidden || self.alpha <= 0.01) { // if (!self.userInteractionEnabled || self.hidden || self.alpha <= 0.01) {
return nil; // return nil;
} // }
CGPoint goButtonPoint = [self.goButton convertPoint:point fromView:self]; // CGPoint goButtonPoint = [self.goButton convertPoint:point fromView:self];
if ([self.goButton pointInside:goButtonPoint withEvent:event]) { // if ([self.goButton pointInside:goButtonPoint withEvent:event]) {
return self.goButton; // return self.goButton;
} // }
// self // // self
return self; // return self;
} //}
@end @end

View File

@@ -73,6 +73,11 @@ static const CGFloat kBannerTopMargin = 0;// 80.0f;
@implementation LuckyGiftWinningBannerView @implementation LuckyGiftWinningBannerView
- (void)dealloc
{
}
+ (void)display:(UIView *)superView + (void)display:(UIView *)superView
inRoomUid:(NSInteger)roomUid inRoomUid:(NSInteger)roomUid
with:(AttachmentModel *)attachment with:(AttachmentModel *)attachment
@@ -102,34 +107,70 @@ exitCurrentRoom:(void(^)(void))exit {
bannerView.currentRoomUid = roomUid; bannerView.currentRoomUid = roomUid;
[superView addSubview:bannerView]; [superView addSubview:bannerView];
[bannerView addNotification];
NSInteger time = 3; NSInteger time = 3;
//#if DEBUG
// time = 3000;
//#endif
@kWeakify(bannerView); @kWeakify(bannerView);
[bannerView popEnterAnimation:^(BOOL finished) { [bannerView popEnterAnimation:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@kStrongify(bannerView); @kStrongify(bannerView);
[bannerView addNotification];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[bannerView popLeaveAnimation:^(bool finished) { [bannerView popLeaveAnimation:^(bool finished) {
if (bannerView.completeDisplay) { if (bannerView.completeDisplay) {
bannerView.completeDisplay(); bannerView.completeDisplay();
} }
[bannerView removeNotification];
[bannerView removeFromSuperview]; [bannerView removeFromSuperview];
}]; }];
}); });
}]; }];
} }
- (void)addNotification { - (void)handleSwipeNotification {
@kWeakify(self);
[[NSNotificationCenter defaultCenter] addObserverForName:@"SwipeOutBanner"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * _Nonnull notification) {
@kStrongify(self);
[self dismissBanner]; [self dismissBanner];
}]; }
- (void)handleTapNotification:(NSNotification *)note {
NSValue *value = note.userInfo[@"point"];
CGPoint point = [value CGPointValue];
// 2/3
CGFloat totalW = KScreenWidth;
CGFloat regionW = totalW * 2.0 / 3.0;
CGFloat originX = (totalW - regionW) / 2.0;
CGRect centerRegion = CGRectMake(originX,
0, // 0
regionW,
self.bounds.size.height);
if (CGRectContainsPoint(centerRegion, point)) {
NSLog(@" Banner tap 点落在中央 2/3 区域");
[self handelTap];
} else {
NSLog(@" Banner tap 点不落在中央 2/3 区域");
// banner
CGPoint screenPoint = [self convertPoint:point toView:nil];
// FunctionContainer
[[NSNotificationCenter defaultCenter] postNotificationName:@"BannerTapToFunctionContainer"
object:nil
userInfo:@{@"point": [NSValue valueWithCGPoint:screenPoint]}];
}
}
- (void)addNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleSwipeNotification)
name:@"SwipeOutBanner"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleTapNotification:)
name:@"TapBanner"
object:nil];
}
- (void)removeNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self];
} }
- (void)dismissBanner { - (void)dismissBanner {
@@ -189,7 +230,7 @@ exitCurrentRoom:(void(^)(void))exit {
[b mas_remakeConstraints:^(MASConstraintMaker *make) { [b mas_remakeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self); make.edges.mas_equalTo(self);
}]; }];
[b addTarget:self action:@selector(handelTap) forControlEvents:UIControlEventTouchUpInside]; // [b addTarget:self action:@selector(handelTap) forControlEvents:UIControlEventTouchUpInside];
} }
return self; return self;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -88,20 +88,21 @@
break; break;
} }
[banner addNotification];
[superView addSubview:banner]; [superView addSubview:banner];
@kWeakify(banner); @kWeakify(banner);
// 使 POP banner // 使 POP banner
[banner popEnterAnimation:^(BOOL finished) { [banner popEnterAnimation:^(BOOL finished) {
@kStrongify(banner);
[banner addNotification];
if (finished) { if (finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@kStrongify(banner);
[banner popLeaveAnimation:^(bool finished) { [banner popLeaveAnimation:^(bool finished) {
if (banner.animationComplete) { if (banner.animationComplete) {
banner.animationComplete(); banner.animationComplete();
} }
[banner removeNotification];
[banner removeFromSuperview]; [banner removeFromSuperview];
}]; }];
}); });
@@ -113,15 +114,37 @@
[self.svgaImageView stopAnimation]; [self.svgaImageView stopAnimation];
} }
- (void)addNotification { - (void)handleSwipeNotification {
@kWeakify(self);
[[NSNotificationCenter defaultCenter] addObserverForName:@"SwipeOutBanner"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * _Nonnull notification) {
@kStrongify(self);
[self dismissBanner]; [self dismissBanner];
}]; }
- (void)handleTapNotification:(NSNotification *)note {
NSValue *value = note.userInfo[@"point"];
CGPoint point = [value CGPointValue];
// banner
CGPoint screenPoint = [self convertPoint:point toView:nil];
// FunctionContainer
[[NSNotificationCenter defaultCenter] postNotificationName:@"BannerTapToFunctionContainer"
object:nil
userInfo:@{@"point": [NSValue valueWithCGPoint:screenPoint]}];
}
- (void)addNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleSwipeNotification)
name:@"SwipeOutBanner"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleTapNotification:)
name:@"TapBanner"
object:nil];
}
- (void)removeNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self];
} }
- (void)dismissBanner { - (void)dismissBanner {
@@ -406,16 +429,16 @@
} }
// ========== 穿 ========== // ========== 穿 ==========
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { //- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.userInteractionEnabled || self.hidden || self.alpha <= 0.01) { // if (!self.userInteractionEnabled || self.hidden || self.alpha <= 0.01) {
return nil; // return nil;
} // }
CGPoint goButtonPoint = [self.goButton convertPoint:point fromView:self]; // CGPoint goButtonPoint = [self.goButton convertPoint:point fromView:self];
if ([self.goButton pointInside:goButtonPoint withEvent:event]) { // if ([self.goButton pointInside:goButtonPoint withEvent:event]) {
return self.goButton; // return self.goButton;
} // }
// self // // self
return self; // return self;
} //}
@end @end

View File

@@ -9,6 +9,16 @@
// MARK: // MARK:
@implementation XPRoomAnimationHitView @implementation XPRoomAnimationHitView
//- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// for (NSInteger i = (self.subviews.count - 1) ; i >= 0 ; i--) {
// UIView * subView = [self.subviews xpSafeObjectAtIndex:i];
// CGPoint convertPoint = [subView convertPoint:point fromView:self];
// if (CGRectContainsPoint(subView.bounds, convertPoint)) {
// return [subView hitTest:convertPoint withEvent:event];
// }
// }
// return nil;
//}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
@@ -17,11 +27,6 @@
return nil; return nil;
} }
// bounds
// if (!CGRectContainsPoint(self.bounds, point)) {
// return nil;
// }
// //
for (NSInteger i = self.subviews.count - 1; i >= 0; i--) { for (NSInteger i = self.subviews.count - 1; i >= 0; i--) {
UIView *subView = [self.subviews xpSafeObjectAtIndex:i]; UIView *subView = [self.subviews xpSafeObjectAtIndex:i];
@@ -35,7 +40,8 @@
} }
} }
// // nil
//
return nil; return nil;
} }

View File

@@ -174,11 +174,94 @@
[self initSubViews]; [self initSubViews];
[self initSubViewConstraints]; [self initSubViewConstraints];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(beginMatchAnchorPK:) name:@"anchorPKMatchBegin" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(beginMatchAnchorPK:) name:@"anchorPKMatchBegin" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleTapToFunctionContainer:) name:@"BannerTapToFunctionContainer" object:nil];
} }
return self; return self;
} }
- (void)handleTapToFunctionContainer:(NSNotification *)note {
NSValue *tapPointValue = note.userInfo[@"point"];
// UIView *bannerContainer = note.userInfo[@"gesture container"];
if (!tapPointValue) {
return;
}
CGPoint tapPoint = [tapPointValue CGPointValue];
// bannerContainer FunctionContainer
CGPoint convertedPoint = [self convertPoint:tapPoint fromView:nil];
NSLog(@"<22><> 坐标转换: 原始点 %@ -> 转换后点 %@", NSStringFromCGPoint(tapPoint), NSStringFromCGPoint(convertedPoint));
//
[self checkPointIntersectionWithSubviews:convertedPoint];
}
- (void)checkPointIntersectionWithSubviews:(CGPoint)point {
// contributeEnterView
if (self.contributeEnterView && !self.contributeEnterView.hidden) {
CGPoint contributePoint = [self.contributeEnterView convertPoint:point fromView:self];
if (CGRectContainsPoint(self.contributeEnterView.bounds, contributePoint)) {
NSLog(@"🎯 点击位置与 contributeEnterView 重合,触发贡献榜事件");
[self contributionButtonAction:nil];
return;
}
}
// onlineView
if (self.onlineView && !self.onlineView.hidden) {
CGPoint onlinePoint = [self.onlineView convertPoint:point fromView:self];
if (CGRectContainsPoint(self.onlineView.bounds, onlinePoint)) {
NSLog(@"🎯 点击位置与 onlineView 重合,触发在线人数事件");
[self onlineTapRecognizer];
return;
}
}
// hourRankEntranceView
if (self.hourRankEntranceView && !self.hourRankEntranceView.hidden) {
CGPoint hourRankPoint = [self.hourRankEntranceView convertPoint:point fromView:self];
if (CGRectContainsPoint(self.hourRankEntranceView.bounds, hourRankPoint)) {
NSLog(@"🎯 点击位置与 hourRankEntranceView 重合,触发小时榜事件");
[self onAnchorHourRankButtonAction:nil];
return;
}
}
// fansTeamEntranceView
if (self.fansTeamEntranceView && !self.fansTeamEntranceView.hidden) {
CGPoint fansTeamPoint = [self.fansTeamEntranceView convertPoint:point fromView:self];
if (CGRectContainsPoint(self.fansTeamEntranceView.bounds, fansTeamPoint)) {
NSLog(@"🎯 点击位置与 fansTeamEntranceView 重合,触发粉丝团事件");
[self tapFansTeamRecognizer];
return;
}
}
// musicEnterButton
if (self.musicEnterButton && !self.musicEnterButton.hidden) {
CGPoint musicPoint = [self.musicEnterButton convertPoint:point fromView:self];
if (CGRectContainsPoint(self.musicEnterButton.bounds, musicPoint)) {
NSLog(@"🎯 点击位置与 musicEnterButton 重合,触发音乐播放器事件");
[self musicEnterButtonAction:self.musicEnterButton];
return;
}
}
// trumpetView
if (self.trumpetView && !self.trumpetView.hidden) {
CGPoint trumpetPoint = [self.trumpetView convertPoint:point fromView:self];
if (CGRectContainsPoint(self.trumpetView.bounds, trumpetPoint)) {
NSLog(@"<22><> 点击位置与 trumpetView 重合,触发小喇叭事件");
// trumpetView
return;
}
}
NSLog(@"❌ 点击位置未与任何功能视图重合");
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// //
if (!self.userInteractionEnabled || self.hidden || self.alpha <= 0.01) { if (!self.userInteractionEnabled || self.hidden || self.alpha <= 0.01) {

View File

@@ -51,34 +51,73 @@ exitCurrentRoom:(void(^)(void))exit {
banner.currentRoomUid = roomUid; banner.currentRoomUid = roomUid;
banner.completeDisplay = complete; banner.completeDisplay = complete;
banner.exitCurrentRoom = exit; banner.exitCurrentRoom = exit;
[banner addNotification];
[superView addSubview:banner]; [superView addSubview:banner];
NSInteger time = 3; NSInteger time = 3;
@kWeakify(banner); @kWeakify(banner);
[banner popEnterAnimation:^(BOOL finished) { [banner popEnterAnimation:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@kStrongify(banner); @kStrongify(banner);
[banner addNotification];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[banner popLeaveAnimation:^(bool finished) { [banner popLeaveAnimation:^(bool finished) {
if (banner.completeDisplay) { if (banner.completeDisplay) {
banner.completeDisplay(); banner.completeDisplay();
} }
[banner removeNotification];
[banner removeFromSuperview]; [banner removeFromSuperview];
}]; }];
}); });
}]; }];
} }
- (void)addNotification { - (void)handleSwipeNotification {
@kWeakify(self);
[[NSNotificationCenter defaultCenter] addObserverForName:@"SwipeOutBanner"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * _Nonnull notification) {
@kStrongify(self);
[self dismissBanner]; [self dismissBanner];
}]; }
- (void)handleTapNotification:(NSNotification *)note {
NSValue *value = note.userInfo[@"point"];
CGPoint point = [value CGPointValue];
// 2/3
CGFloat totalW = KScreenWidth;
CGFloat regionW = totalW * 2.0 / 3.0;
CGFloat originX = (totalW - regionW) / 2.0;
CGRect centerRegion = CGRectMake(originX,
0, // 0
regionW,
self.bounds.size.height);
if (CGRectContainsPoint(centerRegion, point)) {
NSLog(@" Banner tap 点落在中央 2/3 区域");
[self handleTap];
} else {
NSLog(@" Banner tap 点不落在中央 2/3 区域");
// banner
CGPoint screenPoint = [self convertPoint:point toView:nil];
// FunctionContainer
[[NSNotificationCenter defaultCenter] postNotificationName:@"BannerTapToFunctionContainer"
object:nil
userInfo:@{@"point": [NSValue valueWithCGPoint:screenPoint]}];
}
}
- (void)addNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleSwipeNotification)
name:@"SwipeOutBanner"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleTapNotification:)
name:@"TapBanner"
object:nil];
}
- (void)removeNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self];
} }
- (void)setModel:(BravoGiftBannerViewModel *)model { - (void)setModel:(BravoGiftBannerViewModel *)model {
@@ -129,18 +168,18 @@ exitCurrentRoom:(void(^)(void))exit {
self = [super initWithFrame:frame]; self = [super initWithFrame:frame];
if (self) { if (self) {
[self setupUI]; [self setupUI];
self.userInteractionEnabled = NO; self.userInteractionEnabled = YES;
UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom]; UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
[self addSubview:b]; [self addSubview:b];
[b mas_remakeConstraints:^(MASConstraintMaker *make) { [b mas_remakeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self); make.edges.mas_equalTo(self);
}]; }];
[b addTarget:self action:@selector(handelTap) forControlEvents:UIControlEventTouchUpInside]; // [b addTarget:self action:@selector(handleTap) forControlEvents:UIControlEventTouchUpInside];
} }
return self; return self;
} }
- (void)handelTap { - (void)handleTap {
if (self.model.roomUid.integerValue == self.currentRoomUid) { if (self.model.roomUid.integerValue == self.currentRoomUid) {
return; return;
} }

View File

@@ -64,17 +64,19 @@ exitCurrentRoom:(void(^)(void))exit {
banner.completeDisplay = complete; banner.completeDisplay = complete;
banner.exitCurrentRoom = exit; banner.exitCurrentRoom = exit;
banner.currentRoomUid = roomUid; banner.currentRoomUid = roomUid;
[banner addNotification];
[superView addSubview:banner]; [superView addSubview:banner];
@kWeakify(banner); @kWeakify(banner);
[banner popEnterAnimation:^(BOOL finished) { [banner popEnterAnimation:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
@kStrongify(banner); @kStrongify(banner);
[banner addNotification];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[banner popLeaveAnimation:^(bool finished) { [banner popLeaveAnimation:^(bool finished) {
if (banner.completeDisplay) { if (banner.completeDisplay) {
banner.completeDisplay(); banner.completeDisplay();
} }
[banner removeNotification];
[banner removeFromSuperview]; [banner removeFromSuperview];
}]; }];
}); });
@@ -100,15 +102,50 @@ exitCurrentRoom:(void(^)(void))exit {
return banner; return banner;
} }
- (void)addNotification { - (void)handleSwipeNotification {
@kWeakify(self);
[[NSNotificationCenter defaultCenter] addObserverForName:@"SwipeOutBanner"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * _Nonnull notification) {
@kStrongify(self);
[self dismissBanner]; [self dismissBanner];
}]; }
- (void)handleTapNotification:(NSNotification *)note {
NSValue *value = note.userInfo[@"point"];
CGPoint point = [value CGPointValue];
NSLog(@"🔄 GameUniversalBannerView: 接收到点击点 %@ (bannerContainer坐标系)", NSStringFromCGPoint(point));
// bannerContainer GameUniversalBannerView
CGPoint bannerPoint = [self convertPoint:point fromView:self.superview];
NSLog(@"🔄 GameUniversalBannerView: 转换为 banner 坐标系 %@", NSStringFromCGPoint(bannerPoint));
NSLog(@"%@", CGRectContainsPoint(self.goButton.frame, bannerPoint) ? @"YES" : @"NO");
// go
CGPoint goButtonPoint = [self.goButton convertPoint:bannerPoint fromView:self];
if ([self.goButton pointInside:goButtonPoint withEvent:nil]) {
NSLog(@"🎯 GameUniversalBannerView: tap 点与 go 按钮重合,触发游戏跳转事件");
[self handleTapGo];
} else {
CGPoint screenPoint = [self convertPoint:point toView:nil];
// FunctionContainer
[[NSNotificationCenter defaultCenter] postNotificationName:@"BannerTapToFunctionContainer"
object:nil
userInfo:@{@"point": [NSValue valueWithCGPoint:screenPoint]}];
}
}
- (void)addNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleSwipeNotification)
name:@"SwipeOutBanner"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleTapNotification:)
name:@"TapBanner"
object:nil];
}
- (void)removeNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self];
} }
- (void)dismissBanner { - (void)dismissBanner {