新增飘屏组件分析文档和新Banner组件架构设计,详细描述了飘屏组件的共同特征、抽象方案及其优势,提供了基类和子类的设计方案,增强了代码的可维护性和扩展性。同时,更新了相关文档以支持新架构的集成和使用。
This commit is contained in:
472
DOC/新Banner组件架构设计.md
Normal file
472
DOC/新Banner组件架构设计.md
Normal file
@@ -0,0 +1,472 @@
|
||||
# 新Banner组件架构设计
|
||||
|
||||
## 设计概述
|
||||
|
||||
基于对现有7种Banner组件的深度分析,设计一套统一的Banner组件架构,包含父类、子类、数据模型和用户反馈机制。
|
||||
|
||||
## 现有组件分析总结
|
||||
|
||||
### 共同UI结构模式
|
||||
|
||||
| 组件 | 背景 | 头像 | 标题/内容 | 礼物图标 | 操作按钮 | 动画效果 |
|
||||
|------|------|------|-----------|----------|----------|----------|
|
||||
| RoomHighValueGiftBannerAnimation | ✓ | ✓ | ✓ | ✓ | ✓ | SVGA |
|
||||
| CPGiftBanner | ✓ | ✓✓(双人) | ✓ | ✓ | - | POP |
|
||||
| BravoGiftBannerView | ✓ | ✓ | ✓ | ✓ | - | SVGA |
|
||||
| LuckyPackageBannerView | ✓ | ✓ | ✓ | - | ✓ | POP |
|
||||
| LuckyGiftWinningBannerView | ✓ | ✓ | ✓ | - | ✓ | POP |
|
||||
| GameUniversalBannerView | ✓ | ✓ | ✓ | ✓ | ✓ | SVGA |
|
||||
| PIUniversalBannerView | ✓ | - | ✓ | - | ✓ | SVGA |
|
||||
|
||||
### 共同数据模式
|
||||
|
||||
- AttachmentModel作为数据源
|
||||
- 专用ViewModel进行数据解析
|
||||
- 完成回调机制
|
||||
- 用户交互跳转
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 1. 基础父类设计
|
||||
|
||||
```objc
|
||||
// YMBaseBannerView.h
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "YMBannerDataProtocol.h"
|
||||
#import "YMBannerDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class AttachmentModel, YMBaseBannerViewModel;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, YMBannerType) {
|
||||
YMBannerTypeHighValueGift, // 高价值礼物
|
||||
YMBannerTypeCPGift, // CP礼物
|
||||
YMBannerTypeBravoGift, // Bravo超级礼物
|
||||
YMBannerTypeLuckyPackage, // 幸运红包
|
||||
YMBannerTypeLuckyWinning, // 幸运中奖
|
||||
YMBannerTypeGameUniversal, // 通用游戏
|
||||
YMBannerTypeUniversal // 通用飘屏
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, YMBannerAnimationType) {
|
||||
YMBannerAnimationTypeSlide, // 滑动动画
|
||||
YMBannerAnimationTypeFade, // 淡入淡出
|
||||
YMBannerAnimationTypeBounce, // 弹跳效果
|
||||
YMBannerAnimationTypeCustom // 自定义动画
|
||||
};
|
||||
|
||||
@interface YMBaseBannerView : UIView
|
||||
|
||||
#pragma mark - 核心属性
|
||||
@property (nonatomic, assign, readonly) YMBannerType bannerType;
|
||||
@property (nonatomic, strong) AttachmentModel *attachment;
|
||||
@property (nonatomic, strong) YMBaseBannerViewModel *viewModel;
|
||||
@property (nonatomic, weak) id<YMBannerDelegate> delegate;
|
||||
|
||||
#pragma mark - UI组件 (子类可选择使用)
|
||||
@property (nonatomic, strong, readonly) UIView *containerView;
|
||||
@property (nonatomic, strong, readonly) UIImageView *backgroundImageView;
|
||||
@property (nonatomic, strong, readonly) NetImageView *avatarImageView;
|
||||
@property (nonatomic, strong, readonly) NetImageView *secondAvatarImageView; // CP双头像
|
||||
@property (nonatomic, strong, readonly) UILabel *titleLabel;
|
||||
@property (nonatomic, strong, readonly) UILabel *subtitleLabel;
|
||||
@property (nonatomic, strong, readonly) UILabel *contentLabel;
|
||||
@property (nonatomic, strong, readonly) NetImageView *iconImageView;
|
||||
@property (nonatomic, strong, readonly) UIButton *actionButton;
|
||||
@property (nonatomic, strong, readonly) SVGAImageView *svgaView;
|
||||
|
||||
#pragma mark - 动画配置
|
||||
@property (nonatomic, assign) YMBannerAnimationType animationType;
|
||||
@property (nonatomic, assign) CGFloat showDuration;
|
||||
@property (nonatomic, assign) CGFloat stayDuration;
|
||||
@property (nonatomic, assign) CGFloat hideDuration;
|
||||
@property (nonatomic, assign) BOOL enableSwipeGesture;
|
||||
|
||||
#pragma mark - 回调
|
||||
@property (nonatomic, copy) void(^onDisplayComplete)(void);
|
||||
@property (nonatomic, copy) void(^onUserTap)(YMBaseBannerView *banner);
|
||||
@property (nonatomic, copy) void(^onActionTap)(YMBaseBannerView *banner);
|
||||
@property (nonatomic, copy) void(^onDismiss)(YMBaseBannerView *banner);
|
||||
|
||||
#pragma mark - 状态
|
||||
@property (nonatomic, assign, readonly) BOOL isDisplaying;
|
||||
@property (nonatomic, assign, readonly) BOOL isDismissed;
|
||||
|
||||
#pragma mark - 类方法
|
||||
+ (instancetype)bannerWithAttachment:(AttachmentModel *)attachment;
|
||||
+ (void)displayInView:(UIView *)superView
|
||||
attachment:(AttachmentModel *)attachment
|
||||
complete:(void(^)(void))complete;
|
||||
|
||||
#pragma mark - 实例方法
|
||||
- (instancetype)initWithBannerType:(YMBannerType)type;
|
||||
- (void)configureWithAttachment:(AttachmentModel *)attachment;
|
||||
- (void)displayInView:(UIView *)superView;
|
||||
- (void)dismissWithAnimation:(BOOL)animated;
|
||||
|
||||
#pragma mark - 子类重写方法
|
||||
- (Class)viewModelClass; // 返回对应的ViewModel类
|
||||
- (void)setupUIComponents; // 设置UI组件
|
||||
- (void)layoutUIComponents; // 布局UI组件
|
||||
- (void)configureWithViewModel:(YMBaseBannerViewModel *)viewModel; // 配置数据
|
||||
- (void)performShowAnimation; // 执行显示动画
|
||||
- (void)performHideAnimation; // 执行隐藏动画
|
||||
- (void)handleUserTap; // 处理用户点击
|
||||
- (void)handleActionTap; // 处理操作按钮点击
|
||||
|
||||
#pragma mark - 用户反馈
|
||||
- (void)reportDisplayEvent; // 上报展示事件
|
||||
- (void)reportClickEvent:(NSString *)action; // 上报点击事件
|
||||
- (void)reportDismissEvent:(NSString *)reason; // 上报消失事件
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
```
|
||||
|
||||
### 2. 基础数据模型
|
||||
|
||||
```objc
|
||||
// YMBaseBannerViewModel.h
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "PIBaseModel.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface YMBaseBannerViewModel : PIBaseModel
|
||||
|
||||
#pragma mark - 基础信息
|
||||
@property (nonatomic, assign) NSInteger roomUid;
|
||||
@property (nonatomic, assign) NSInteger targetRoomUid;
|
||||
@property (nonatomic, copy) NSString *title;
|
||||
@property (nonatomic, copy) NSString *subtitle;
|
||||
@property (nonatomic, copy) NSString *content;
|
||||
|
||||
#pragma mark - 用户信息
|
||||
@property (nonatomic, copy) NSString *senderNick;
|
||||
@property (nonatomic, copy) NSString *senderAvatar;
|
||||
@property (nonatomic, assign) NSInteger senderUid;
|
||||
@property (nonatomic, copy) NSString *receiverNick;
|
||||
@property (nonatomic, copy) NSString *receiverAvatar;
|
||||
@property (nonatomic, assign) NSInteger receiverUid;
|
||||
|
||||
#pragma mark - 视觉资源
|
||||
@property (nonatomic, copy) NSString *backgroundImageUrl;
|
||||
@property (nonatomic, copy) NSString *iconImageUrl;
|
||||
@property (nonatomic, copy) NSString *svgaUrl;
|
||||
|
||||
#pragma mark - 交互配置
|
||||
@property (nonatomic, copy) NSString *actionText;
|
||||
@property (nonatomic, copy) NSString *skipUrl;
|
||||
@property (nonatomic, assign) BOOL enableClick;
|
||||
@property (nonatomic, assign) BOOL enableAction;
|
||||
|
||||
#pragma mark - 埋点数据
|
||||
@property (nonatomic, copy) NSString *eventType;
|
||||
@property (nonatomic, strong) NSDictionary *trackingData;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
```
|
||||
|
||||
### 3. 代理协议设计
|
||||
|
||||
```objc
|
||||
// YMBannerDelegate.h
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class YMBaseBannerView;
|
||||
|
||||
@protocol YMBannerDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
#pragma mark - 生命周期回调
|
||||
- (void)bannerWillDisplay:(YMBaseBannerView *)banner;
|
||||
- (void)bannerDidDisplay:(YMBaseBannerView *)banner;
|
||||
- (void)bannerWillDismiss:(YMBaseBannerView *)banner;
|
||||
- (void)bannerDidDismiss:(YMBaseBannerView *)banner;
|
||||
|
||||
#pragma mark - 交互回调
|
||||
- (void)banner:(YMBaseBannerView *)banner didTapWithAction:(NSString *)action;
|
||||
- (void)banner:(YMBaseBannerView *)banner willNavigateToRoom:(NSInteger)roomUid;
|
||||
- (void)banner:(YMBaseBannerView *)banner willOpenURL:(NSString *)url;
|
||||
|
||||
#pragma mark - 数据回调
|
||||
- (void)banner:(YMBaseBannerView *)banner didReportEvent:(NSString *)event data:(NSDictionary *)data;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
```
|
||||
|
||||
## 子类设计
|
||||
|
||||
### 1. 高价值礼物Banner
|
||||
|
||||
```objc
|
||||
// YMHighValueGiftBannerView.h
|
||||
#import "YMBaseBannerView.h"
|
||||
|
||||
@interface YMHighValueGiftBannerView : YMBaseBannerView
|
||||
|
||||
@property (nonatomic, strong, readonly) UILabel *giftNameLabel;
|
||||
@property (nonatomic, strong, readonly) UILabel *giftCountLabel;
|
||||
@property (nonatomic, strong, readonly) MarqueeLabel *senderScrollLabel;
|
||||
@property (nonatomic, strong, readonly) MarqueeLabel *roomNameScrollLabel;
|
||||
|
||||
@end
|
||||
|
||||
// YMHighValueGiftBannerViewModel.h
|
||||
@interface YMHighValueGiftBannerViewModel : YMBaseBannerViewModel
|
||||
|
||||
@property (nonatomic, copy) NSString *giftName;
|
||||
@property (nonatomic, assign) NSInteger giftCount;
|
||||
@property (nonatomic, copy) NSString *giftImageUrl;
|
||||
@property (nonatomic, assign) NSInteger bgLevel;
|
||||
@property (nonatomic, copy) NSString *roomTitle;
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
### 2. CP礼物Banner
|
||||
|
||||
```objc
|
||||
// YMCPGiftBannerView.h
|
||||
#import "YMBaseBannerView.h"
|
||||
|
||||
@interface YMCPGiftBannerView : YMBaseBannerView
|
||||
|
||||
@property (nonatomic, strong, readonly) UIStackView *cpStackView;
|
||||
@property (nonatomic, strong, readonly) UILabel *relationLabel;
|
||||
|
||||
@end
|
||||
|
||||
// YMCPGiftBannerViewModel.h
|
||||
@interface YMCPGiftBannerViewModel : YMBaseBannerViewModel
|
||||
|
||||
@property (nonatomic, copy) NSString *giftImageUrl;
|
||||
@property (nonatomic, copy) NSString *relationText;
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
### 3. 通用游戏Banner
|
||||
|
||||
```objc
|
||||
// YMGameUniversalBannerView.h
|
||||
#import "YMBaseBannerView.h"
|
||||
|
||||
@interface YMGameUniversalBannerView : YMBaseBannerView
|
||||
|
||||
@property (nonatomic, strong, readonly) NetImageView *gameIconView;
|
||||
@property (nonatomic, assign) NSInteger gameID;
|
||||
|
||||
@end
|
||||
|
||||
// YMGameUniversalBannerViewModel.h
|
||||
@interface YMGameUniversalBannerViewModel : YMBaseBannerViewModel
|
||||
|
||||
@property (nonatomic, copy) NSString *gameIconUrl;
|
||||
@property (nonatomic, assign) NSInteger gameID;
|
||||
@property (nonatomic, copy) NSString *gameTitle;
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
## 用户反馈机制设计
|
||||
|
||||
### 1. 反馈管理器
|
||||
|
||||
```objc
|
||||
// YMBannerFeedbackManager.h
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, YMBannerFeedbackType) {
|
||||
YMBannerFeedbackTypeDisplay, // 展示
|
||||
YMBannerFeedbackTypeClick, // 点击
|
||||
YMBannerFeedbackTypeAction, // 操作
|
||||
YMBannerFeedbackTypeDismiss, // 消失
|
||||
YMBannerFeedbackTypeError // 错误
|
||||
};
|
||||
|
||||
@interface YMBannerFeedbackManager : NSObject
|
||||
|
||||
+ (instancetype)shared;
|
||||
|
||||
#pragma mark - 事件上报
|
||||
- (void)reportBannerEvent:(YMBannerFeedbackType)type
|
||||
banner:(YMBaseBannerView *)banner
|
||||
data:(NSDictionary *)data;
|
||||
|
||||
#pragma mark - 性能监控
|
||||
- (void)startPerformanceMonitoring:(YMBaseBannerView *)banner;
|
||||
- (void)endPerformanceMonitoring:(YMBaseBannerView *)banner;
|
||||
|
||||
#pragma mark - 错误收集
|
||||
- (void)reportError:(NSError *)error
|
||||
banner:(YMBaseBannerView *)banner
|
||||
context:(NSDictionary *)context;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
```
|
||||
|
||||
### 2. 埋点事件定义
|
||||
|
||||
```objc
|
||||
// YMBannerTrackingEvents.h
|
||||
extern NSString * const kYMBannerEventDisplay; // banner_display
|
||||
extern NSString * const kYMBannerEventClick; // banner_click
|
||||
extern NSString * const kYMBannerEventAction; // banner_action
|
||||
extern NSString * const kYMBannerEventDismiss; // banner_dismiss
|
||||
extern NSString * const kYMBannerEventLoadStart; // banner_load_start
|
||||
extern NSString * const kYMBannerEventLoadEnd; // banner_load_end
|
||||
extern NSString * const kYMBannerEventError; // banner_error
|
||||
|
||||
// 埋点参数Key
|
||||
extern NSString * const kYMBannerTrackingKeyType; // banner_type
|
||||
extern NSString * const kYMBannerTrackingKeyRoomUid; // room_uid
|
||||
extern NSString * const kYMBannerTrackingKeyDuration; // duration
|
||||
extern NSString * const kYMBannerTrackingKeyAction; // action
|
||||
extern NSString * const kYMBannerTrackingKeyReason; // reason
|
||||
```
|
||||
|
||||
## 上下文调整说明
|
||||
|
||||
### 1. RoomAnimationView.m 调整
|
||||
|
||||
#### 1.1 替换现有Banner创建逻辑
|
||||
|
||||
```objc
|
||||
// 原代码
|
||||
- (void)playRoomGiftBanner:(AttachmentModel *)obj {
|
||||
[RoomHighValueGiftBannerAnimation display:self.bannerContainer
|
||||
with:obj
|
||||
complete:^{
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self processNextRoomEffectAttachment];
|
||||
}];
|
||||
}
|
||||
|
||||
// 新代码
|
||||
- (void)playRoomGiftBanner:(AttachmentModel *)obj {
|
||||
YMHighValueGiftBannerView *banner = [YMHighValueGiftBannerView bannerWithAttachment:obj];
|
||||
banner.delegate = self;
|
||||
banner.onDisplayComplete = ^{
|
||||
self.isRoomBannerV2Displaying = NO;
|
||||
[self processNextRoomEffectAttachment];
|
||||
};
|
||||
[banner displayInView:self.bannerContainer];
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 添加代理实现
|
||||
|
||||
```objc
|
||||
#pragma mark - YMBannerDelegate
|
||||
- (void)banner:(YMBaseBannerView *)banner didReportEvent:(NSString *)event data:(NSDictionary *)data {
|
||||
[[YMBannerFeedbackManager shared] reportBannerEvent:YMBannerFeedbackTypeDisplay
|
||||
banner:banner
|
||||
data:data];
|
||||
}
|
||||
|
||||
- (void)banner:(YMBaseBannerView *)banner willNavigateToRoom:(NSInteger)roomUid {
|
||||
// 处理房间跳转逻辑
|
||||
RoomInfoModel *currentRoom = self.hostDelegate.getRoomInfo;
|
||||
if (currentRoom.uid != roomUid) {
|
||||
// 执行跨房间跳转
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 项目依赖调整
|
||||
|
||||
#### 2.1 新增文件结构
|
||||
|
||||
```
|
||||
YuMi/Modules/YMRoom/View/Banner/
|
||||
├── Base/
|
||||
│ ├── YMBaseBannerView.h/.m
|
||||
│ ├── YMBaseBannerViewModel.h/.m
|
||||
│ └── YMBannerDelegate.h
|
||||
├── Subclasses/
|
||||
│ ├── YMHighValueGiftBannerView.h/.m
|
||||
│ ├── YMCPGiftBannerView.h/.m
|
||||
│ ├── YMBravoGiftBannerView.h/.m
|
||||
│ ├── YMGameUniversalBannerView.h/.m
|
||||
│ └── YMLuckyPackageBannerView.h/.m
|
||||
├── Manager/
|
||||
│ └── YMBannerFeedbackManager.h/.m
|
||||
└── Constants/
|
||||
└── YMBannerTrackingEvents.h/.m
|
||||
```
|
||||
|
||||
#### 2.2 Podfile依赖更新
|
||||
|
||||
```ruby
|
||||
# 确保动画库版本兼容
|
||||
pod 'pop', '~> 1.0'
|
||||
pod 'SVGAPlayer', '~> 2.3'
|
||||
|
||||
# 新增性能监控
|
||||
pod 'YMPerformanceMonitor' # 如果有自定义性能监控库
|
||||
```
|
||||
|
||||
### 3. 配置文件调整
|
||||
|
||||
#### 3.1 添加Banner配置
|
||||
|
||||
```objc
|
||||
// YMBannerConfig.h
|
||||
@interface YMBannerConfig : NSObject
|
||||
|
||||
@property (nonatomic, assign) BOOL enablePerformanceMonitoring;
|
||||
@property (nonatomic, assign) BOOL enableErrorReporting;
|
||||
@property (nonatomic, assign) CGFloat defaultShowDuration;
|
||||
@property (nonatomic, assign) CGFloat defaultStayDuration;
|
||||
@property (nonatomic, assign) CGFloat defaultHideDuration;
|
||||
|
||||
+ (instancetype)shared;
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
### 4. 迁移策略
|
||||
|
||||
#### 4.1 Phase 1: 基础架构
|
||||
|
||||
- 创建基类和协议
|
||||
- 实现反馈管理器
|
||||
- 设置基础配置
|
||||
|
||||
#### 4.2 Phase 2: 渐进迁移
|
||||
|
||||
- 优先迁移使用频率最高的Banner
|
||||
- 保持向后兼容性
|
||||
- 添加单元测试
|
||||
|
||||
#### 4.3 Phase 3: 优化完善
|
||||
|
||||
- 移除旧代码
|
||||
- 性能优化
|
||||
- 文档完善
|
||||
|
||||
## 总结
|
||||
|
||||
这套新的Banner组件架构具备以下优势:
|
||||
|
||||
1. **统一性**: 提供一致的API和行为模式
|
||||
2. **扩展性**: 易于添加新的Banner类型
|
||||
3. **可维护性**: 集中管理动画、埋点和反馈
|
||||
4. **性能优化**: 统一的资源管理和内存优化
|
||||
5. **用户体验**: 标准化的交互和反馈机制
|
||||
|
||||
通过这套架构,可以显著提升Banner组件的开发效率和用户体验质量。
|
80
DOC/飘屏组件分析文档.md
Normal file
80
DOC/飘屏组件分析文档.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# 飘屏组件分析文档
|
||||
|
||||
## 概述
|
||||
|
||||
本文档详细分析了项目中在 `RoomAnimationView.m` 的 `bannerContainer` 中展示的所有飘屏类型及其触发逻辑。
|
||||
|
||||
## 飘屏展示机制
|
||||
|
||||
- **展示容器**: `bannerContainer` (XPRoomAnimationHitView)
|
||||
- **位置**: 视图层级最顶层,距离顶部导航栏高度
|
||||
- **尺寸**: 屏幕宽度 × 180px 高度
|
||||
- **管理机制**: 统一队列管理,按优先级顺序播放
|
||||
|
||||
## 飘屏类型详细表格
|
||||
|
||||
| 序号 | 飘屏名称 | 类名 | 触发消息类型 | 处理方法 | 业务场景 | 功能描述 |
|
||||
|-----|---------|------|-------------|---------|----------|---------|
|
||||
| 1 | 高价值礼物飘屏 | RoomHighValueGiftBannerAnimation | Custom_Message_Sub_Gift_ChannelNotify | playRoomGiftBanner: | 礼物系统 | 展示高价值礼物的全服广播 |
|
||||
| 2 | CP礼物飘屏 | CPGiftBanner | Custom_Message_Sub_CP_Gift | playCPGiftBanner: | CP系统 | 展示CP相关礼物特效 |
|
||||
| 3 | Bravo超级礼物飘屏 | BravoGiftBannerView | Custom_Message_Sub_Super_Gift_Banner | playBroveBanner: | 超级礼物 | 展示Bravo超级礼物效果 |
|
||||
| 4 | 幸运红包飘屏 | LuckyPackageBannerView | Custom_Message_Sub_LuckyPackage | playLuckyPackageBanner: | 红包系统 | 展示房间红包,支持跨房间跳转 |
|
||||
| 5 | 幸运礼物中奖飘屏 | LuckyGiftWinningBannerView | Custom_Message_Sub_Super_Gift_Winning_Coins_ALL_Room | playLuckyWinningBanner: | 中奖系统 | 展示全服中奖信息 |
|
||||
| 6 | 通用游戏飘屏 | GameUniversalBannerView | Custom_Message_Sub_General_Floating_Screen_One_Room<br/>Custom_Message_Sub_General_Floating_Screen_All_Room | playGameBanner: | 游戏系统 | 展示游戏相关飘屏,支持跳转 |
|
||||
| 7 | 塔罗飘屏 | XPRoomTarrowBannerView | Custom_Message_Sub_Tarot_Advanced<br/>Custom_Message_Sub_Tarot_Intermediate | createTarotBannerAnimation: | 塔罗活动 | 展示塔罗相关活动信息 |
|
||||
| 8 | 星厨房飘屏 | XPRoomStarKitchenBannerView | Custom_Message_Sub_Star_Kitchen_FullScreen | createStarKitchenBannerAnimation: | 厨房活动 | 展示星厨房活动 |
|
||||
| 9 | 夺宝精灵飘屏 | - | Custom_Message_Sub_Treasure_Fairy_Draw_Gift_L4<br/>Custom_Message_Sub_Treasure_Fairy_Draw_Gift_L5<br/>Custom_Message_Sub_Treasure_Fairy_Convert_L1/L2/L3 | createTreasureFairyBannerAnimation: | 夺宝活动 | 展示夺宝精灵高等级奖励 |
|
||||
| 10 | 主播小时榜飘屏 | XPRoomAnchorRankBannerView | Custom_Message_Sub_Anchor_Hour_Rank | 创建主播排行榜飘屏 | 排行榜 | 展示主播小时榜信息 |
|
||||
| 11 | 通用H5飘屏 | XPRoomTarrowBannerView | Custom_Message_Sub_Common_H5_Novice<br/>Custom_Message_Sub_Common_H5_Advanced | createCommonH5BannerAnimation: | H5活动 | 展示通用H5活动飘屏 |
|
||||
| 12 | 通用飘屏 | PIUniversalBannerView | 多种消息类型 | createGeneralFloatingScreenAnimation: | 通用展示 | 提供通用飘屏展示能力 |
|
||||
|
||||
## 队列管理机制
|
||||
|
||||
### 核心属性
|
||||
|
||||
- **队列数组**: `roomBannertModelsQueueV2` (NSMutableArray)
|
||||
- **状态标记**: `isRoomBannerV2Displaying` (BOOL)
|
||||
|
||||
### 处理流程
|
||||
|
||||
1. **消息接收**: 调用 `inserBannerModelToQueue:` 将消息加入队列
|
||||
2. **优先级排序**: 调用 `sortBannerQueue` 按消息类型的second值排序(数值越小优先级越高)
|
||||
3. **顺序播放**: 调用 `processNextRoomEffectAttachment` 逐个播放
|
||||
4. **播放完成**: 每个飘屏播放完成后,设置 `isRoomBannerV2Displaying = NO` 并处理下一个
|
||||
|
||||
### 优先级规则
|
||||
|
||||
飘屏按 `AttachmentModel.second` 值进行排序,确保重要消息优先展示。
|
||||
|
||||
## 消息类型分类
|
||||
|
||||
### 礼物相关
|
||||
|
||||
- Custom_Message_Sub_Gift_ChannelNotify (32)
|
||||
- Custom_Message_Sub_CP_Gift
|
||||
- Custom_Message_Sub_Super_Gift_Banner (1066)
|
||||
|
||||
### 红包/福袋相关
|
||||
|
||||
- Custom_Message_Sub_LuckyPackage (607)
|
||||
- Custom_Message_Sub_Super_Gift_Winning_Coins_ALL_Room (1063)
|
||||
|
||||
### 游戏相关
|
||||
|
||||
- Custom_Message_Sub_General_Floating_Screen_One_Room (1071)
|
||||
- Custom_Message_Sub_General_Floating_Screen_All_Room (1072)
|
||||
|
||||
### 活动相关
|
||||
|
||||
- Custom_Message_Sub_Tarot_Advanced (714)
|
||||
- Custom_Message_Sub_Tarot_Intermediate (713)
|
||||
- Custom_Message_Sub_Star_Kitchen_FullScreen (1042)
|
||||
- Custom_Message_Sub_Treasure_Fairy_* (9700系列)
|
||||
|
||||
### 排行榜相关
|
||||
|
||||
- Custom_Message_Sub_Anchor_Hour_Rank (891)
|
||||
|
||||
### 通用相关
|
||||
|
||||
- Custom_Message_Sub_Common_H5_* (1100系列)
|
203
DOC/飘屏组件抽象分析.md
Normal file
203
DOC/飘屏组件抽象分析.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# 飘屏组件抽象分析
|
||||
|
||||
## 分析概述
|
||||
|
||||
基于对项目中12种飘屏组件的深入分析,发现这些组件存在高度相似的设计模式和实现结构,具备抽象出统一父类的条件。
|
||||
|
||||
## 现状分析
|
||||
|
||||
### 共同特征
|
||||
|
||||
经过代码分析,发现所有飘屏组件都具有以下共同特征:
|
||||
|
||||
#### 1. 统一的类方法签名模式
|
||||
|
||||
大部分Banner类都采用了相似的类方法签名:
|
||||
|
||||
``` objc
|
||||
+ (void)display:(UIView *)superView
|
||||
with:(AttachmentModel *)attachment
|
||||
complete:(void(^)(void))complete;
|
||||
|
||||
// 或者带房间信息的版本
|
||||
+ (void)display:(UIView *)superView
|
||||
inRoomUid:(NSInteger)roomUid
|
||||
with:(AttachmentModel *)attachment
|
||||
complete:(void(^)(void))complete
|
||||
exitCurrentRoom:(void(^)(void))exit;
|
||||
```
|
||||
|
||||
#### 2. 共同的内部属性结构
|
||||
|
||||
```objc
|
||||
@property (nonatomic, strong) SomeViewModel *model; // 数据模型
|
||||
@property (nonatomic, strong) UIImageView *backgroundImageView; // 背景图片
|
||||
@property (nonatomic, copy) void(^completeDisplay)(void); // 完成回调
|
||||
@property (nonatomic, copy) void(^exitCurrentRoom)(void); // 退房回调
|
||||
@property (nonatomic, assign) NSInteger currentRoomUid; // 当前房间ID
|
||||
```
|
||||
|
||||
#### 3. 相似的动画流程
|
||||
|
||||
- 从屏幕右侧滑入 (x = KScreenWidth)
|
||||
- 短暂停留展示
|
||||
- 滑出屏幕或淡出
|
||||
- 执行完成回调
|
||||
|
||||
#### 4. 统一的数据处理模式
|
||||
|
||||
- 都依赖 `AttachmentModel` 作为数据源
|
||||
- 都创建对应的ViewModel进行数据解析
|
||||
- 都继承自 `PIBaseModel`
|
||||
|
||||
#### 5. 相似的UI组件
|
||||
|
||||
- 背景图片视图
|
||||
- 用户头像
|
||||
- 文本标签
|
||||
- 可选的SVGA动画视图
|
||||
- 可选的交互按钮
|
||||
|
||||
## 抽象方案设计
|
||||
|
||||
### 建议的基类结构: `BaseRoomBannerView`
|
||||
|
||||
```objc
|
||||
@interface BaseRoomBannerView : UIView
|
||||
|
||||
#pragma mark - 核心属性
|
||||
@property (nonatomic, strong) AttachmentModel *attachment;
|
||||
@property (nonatomic, strong) PIBaseModel *viewModel;
|
||||
@property (nonatomic, assign) NSInteger currentRoomUid;
|
||||
@property (nonatomic, assign) NSInteger targetRoomUid;
|
||||
|
||||
#pragma mark - UI组件
|
||||
@property (nonatomic, strong) UIView *containerView;
|
||||
@property (nonatomic, strong) UIImageView *backgroundImageView;
|
||||
@property (nonatomic, strong) NetImageView *avatarImageView;
|
||||
@property (nonatomic, strong) UILabel *titleLabel;
|
||||
@property (nonatomic, strong) UILabel *subTitleLabel;
|
||||
@property (nonatomic, strong) UIButton *actionButton;
|
||||
@property (nonatomic, strong) SVGAImageView *svgaView;
|
||||
|
||||
#pragma mark - 动画配置
|
||||
@property (nonatomic, assign) CGFloat showDuration;
|
||||
@property (nonatomic, assign) CGFloat stayDuration;
|
||||
@property (nonatomic, assign) CGFloat hideDuration;
|
||||
@property (nonatomic, assign) BOOL useSlideAnimation;
|
||||
@property (nonatomic, assign) BOOL useFadeAnimation;
|
||||
|
||||
#pragma mark - 回调
|
||||
@property (nonatomic, copy) void(^completeDisplay)(void);
|
||||
@property (nonatomic, copy) void(^exitCurrentRoom)(void);
|
||||
@property (nonatomic, copy) void(^didTapBanner)(NSInteger roomID);
|
||||
|
||||
#pragma mark - 类方法
|
||||
+ (instancetype)createWithAttachment:(AttachmentModel *)attachment
|
||||
inRoomUid:(NSInteger)roomUid
|
||||
complete:(void(^)(void))complete
|
||||
exitCurrentRoom:(void(^)(void))exit;
|
||||
|
||||
+ (void)display:(UIView *)superView
|
||||
with:(AttachmentModel *)attachment
|
||||
complete:(void(^)(void))complete;
|
||||
|
||||
+ (void)display:(UIView *)superView
|
||||
inRoomUid:(NSInteger)roomUid
|
||||
with:(AttachmentModel *)attachment
|
||||
complete:(void(^)(void))complete
|
||||
exitCurrentRoom:(void(^)(void))exit;
|
||||
|
||||
#pragma mark - 实例方法
|
||||
- (void)setupWithAttachment:(AttachmentModel *)attachment;
|
||||
- (void)showInSuperView:(UIView *)superView;
|
||||
- (void)performShowAnimation;
|
||||
- (void)performHideAnimation;
|
||||
|
||||
#pragma mark - 子类重写方法
|
||||
- (Class)viewModelClass; // 返回对应的ViewModel类
|
||||
- (void)setupUI; // 设置UI布局
|
||||
- (void)configureWithModel:(PIBaseModel *)model; // 配置数据
|
||||
- (void)customizeAnimation; // 自定义动画
|
||||
- (void)handleTapAction; // 处理点击事件
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
### 抽象优势分析
|
||||
|
||||
#### 1. 代码复用率提升
|
||||
|
||||
- 消除重复的动画逻辑
|
||||
- 统一的数据处理流程
|
||||
- 共享的UI组件管理
|
||||
|
||||
#### 2. 维护成本降低
|
||||
|
||||
- 集中管理动画参数
|
||||
- 统一的回调处理机制
|
||||
- 标准化的生命周期管理
|
||||
|
||||
#### 3. 扩展性增强
|
||||
|
||||
- 新增飘屏类型只需继承基类
|
||||
- 便于添加通用功能(如埋点、性能监控)
|
||||
- 支持主题切换等全局功能
|
||||
|
||||
#### 4. 一致性保证
|
||||
|
||||
- 统一的动画效果和时长
|
||||
- 标准化的交互行为
|
||||
- 一致的视觉表现
|
||||
|
||||
### 实现策略
|
||||
|
||||
#### 阶段一:创建基类
|
||||
|
||||
1. 提取共同属性和方法
|
||||
2. 实现通用动画逻辑
|
||||
3. 定义子类接口规范
|
||||
|
||||
#### 阶段二:重构现有类
|
||||
|
||||
1. 逐步迁移现有Banner类继承基类
|
||||
2. 移除重复代码
|
||||
3. 保持向后兼容
|
||||
|
||||
#### 阶段三:优化和扩展
|
||||
|
||||
1. 添加性能监控
|
||||
2. 实现主题支持
|
||||
3. 统一埋点逻辑
|
||||
|
||||
### 潜在挑战
|
||||
|
||||
#### 1. 兼容性问题
|
||||
|
||||
- 现有代码的重构风险
|
||||
- 不同Banner的特殊需求差异
|
||||
- 第三方依赖的适配
|
||||
|
||||
#### 2. 性能考虑
|
||||
|
||||
- 基类功能过于复杂可能影响性能
|
||||
- 内存占用的优化
|
||||
- 动画性能的平衡
|
||||
|
||||
#### 3. 维护复杂度
|
||||
|
||||
- 基类变更可能影响所有子类
|
||||
- 需要详细的文档和测试
|
||||
- 团队学习成本
|
||||
|
||||
## 结论
|
||||
|
||||
**建议进行抽象**:项目中的飘屏组件具有高度的相似性,抽象出基类能够显著提升代码质量和开发效率。建议采用渐进式重构策略,先创建基类和接口规范,再逐步迁移现有实现,确保系统稳定性的同时实现架构优化。
|
||||
|
||||
## 优先级建议
|
||||
|
||||
1. **高优先级**:创建 `BaseRoomBannerView` 基类
|
||||
2. **中优先级**:重构使用频率最高的Banner类
|
||||
3. **低优先级**:添加扩展功能和优化
|
||||
|
||||
通过这种抽象设计,能够在保持现有功能完整性的同时,为未来的功能扩展和维护奠定良好的架构基础。
|
@@ -260,3 +260,4 @@ NS_ASSUME_NONNULL_END
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -406,3 +406,4 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -338,6 +338,7 @@
|
||||
if ([sessionId isEqualToString:self.currentPublicRoomId]) {
|
||||
NSLog(@"PublicRoomManager: 收到公共房间消息: %@", message.text);
|
||||
// 这里可以添加公共房间消息的处理逻辑
|
||||
// TODO: 全服广播转移到此类
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2242,6 +2242,9 @@ XPRoomGraffitiGiftAnimationViewDelegate
|
||||
- (XPRoomAnimationHitView *)bannerContainer {
|
||||
if (!_bannerContainer) {
|
||||
_bannerContainer = [[XPRoomAnimationHitView alloc] init];
|
||||
#if DEBUG
|
||||
_bannerContainer.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
|
||||
#endif
|
||||
}
|
||||
return _bannerContainer;
|
||||
}
|
||||
|
@@ -42,6 +42,12 @@ typedef NS_ENUM(NSInteger, SendGiftType) {
|
||||
// 强制重置连击状态
|
||||
- (void)forceBoomStateReset;
|
||||
|
||||
#if DEBUG
|
||||
// 调试工具
|
||||
- (void)simulateComboViewDisappear;
|
||||
- (void)simulateNetworkFailureDuringCombo;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@@ -184,6 +184,40 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 调试工具
|
||||
|
||||
#if DEBUG
|
||||
// 模拟连击UI消失异常
|
||||
- (void)simulateComboViewDisappear {
|
||||
NSLog(@"🔴 [调试] 模拟连击UI消失异常");
|
||||
|
||||
// 模拟连击面板被意外移除
|
||||
if (self.comboView && self.comboView.superview) {
|
||||
[self.comboView removeFromSuperview];
|
||||
self.comboView = nil;
|
||||
NSLog(@"🔴 模拟异常:连击UI已消失但状态未重置");
|
||||
NSLog(@" 当前连击状态:%@", [[GiftComboManager sharedManager] isGiftCombing] ? @"进行中" : @"未进行");
|
||||
} else {
|
||||
NSLog(@"⚠️ 当前没有连击面板可以移除");
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟网络异常导致的连击错误
|
||||
- (void)simulateNetworkFailureDuringCombo {
|
||||
NSLog(@"🔴 [调试] 模拟网络异常导致连击错误");
|
||||
|
||||
// 先确保连击状态开启
|
||||
[[GiftComboManager sharedManager] enableToCombo:YES];
|
||||
[[GiftComboManager sharedManager] resetCombo];
|
||||
|
||||
// 模拟网络请求失败,直接调用强制移除
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[[GiftComboManager sharedManager] forceRemove];
|
||||
NSLog(@"🔴 已模拟网络异常,触发强制移除");
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
- (instancetype)initWithType:(SendGiftType)type uid:(NSString * __nullable)uid{
|
||||
if (self = [super init]) {
|
||||
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
|
||||
@@ -203,6 +237,14 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
|
||||
name:kBoomStateForceResetNotification
|
||||
object:nil];
|
||||
|
||||
#if DEBUG
|
||||
// 注册调试通知
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(simulateComboViewDisappear)
|
||||
name:@"DebugSimulateComboViewDisappear"
|
||||
object:nil];
|
||||
#endif
|
||||
|
||||
[self initSubViews];
|
||||
[self initSubViewConstraints];
|
||||
[self initHttpRequest];
|
||||
|
@@ -345,11 +345,10 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
[self setupForBoom];
|
||||
|
||||
[self handleGiftComboCallBack];
|
||||
|
||||
//#if DEBUG
|
||||
// UIButton *b = [UIButton buttonWithType:UIButtonTypeInfoLight];
|
||||
// b.frame = CGRectMake(100, 100, 100, 100);
|
||||
// [self.view addSubview:b];
|
||||
// [b addTarget:self action:@selector(test) forControlEvents:UIControlEventTouchUpInside];
|
||||
// // 添加调试工具
|
||||
// [self setupDebugButtons];
|
||||
//#endif
|
||||
}
|
||||
//- (void)test {
|
||||
@@ -564,6 +563,174 @@ XPCandyTreeInsufficientBalanceViewDelegate>
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 调试工具
|
||||
|
||||
#if DEBUG
|
||||
// 模拟状态不一致异常
|
||||
- (void)simulateStateInconsistency {
|
||||
NSLog(@"🔴 [调试] 模拟状态不一致异常");
|
||||
|
||||
// 模拟UI隐藏但连击状态不匹配
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.sideMenu.hidden = YES;
|
||||
self.menuContainerView.hidden = YES;
|
||||
NSLog(@"🔴 已隐藏底部栏和侧栏");
|
||||
|
||||
// 检查连击状态
|
||||
BOOL isCombing = [[GiftComboManager sharedManager] isGiftCombing];
|
||||
NSLog(@" 当前连击状态:%@", isCombing ? @"进行中" : @"未进行");
|
||||
|
||||
if (!isCombing) {
|
||||
NSLog(@"🔴 检测到状态不一致:UI已隐藏但连击未进行");
|
||||
// 触发保护机制
|
||||
[self forceBoomStateReset];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 模拟应用进入后台异常
|
||||
- (void)simulateAppEnterBackground {
|
||||
NSLog(@"🔴 [调试] 模拟应用进入后台异常");
|
||||
|
||||
// 先启动连击状态
|
||||
[[GiftComboManager sharedManager] enableToCombo:YES];
|
||||
[[GiftComboManager sharedManager] resetCombo];
|
||||
NSLog(@"🔴 已启动连击状态");
|
||||
|
||||
// 延迟1秒后模拟进入后台
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
NSLog(@"🔴 模拟应用进入后台...");
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidEnterBackgroundNotification
|
||||
object:nil];
|
||||
});
|
||||
}
|
||||
|
||||
// 模拟内存警告异常
|
||||
- (void)simulateMemoryWarning {
|
||||
NSLog(@"🔴 [调试] 模拟内存警告异常");
|
||||
|
||||
// 先启动连击状态
|
||||
[[GiftComboManager sharedManager] enableToCombo:YES];
|
||||
[[GiftComboManager sharedManager] resetCombo];
|
||||
NSLog(@"🔴 已启动连击状态");
|
||||
|
||||
// 延迟1秒后模拟内存警告
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
NSLog(@"🔴 模拟内存警告...");
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification
|
||||
object:nil];
|
||||
});
|
||||
}
|
||||
|
||||
// 模拟礼物面板UI消失
|
||||
- (void)simulateGiftViewUIDisappear {
|
||||
NSLog(@"🔴 [调试] 模拟礼物面板UI消失");
|
||||
|
||||
// 通过通知获取当前的礼物面板
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"DebugSimulateComboViewDisappear"
|
||||
object:nil];
|
||||
}
|
||||
|
||||
// 模拟网络异常
|
||||
- (void)simulateNetworkError {
|
||||
NSLog(@"🔴 [调试] 模拟网络异常");
|
||||
|
||||
// 先启动连击状态
|
||||
[[GiftComboManager sharedManager] enableToCombo:YES];
|
||||
[[GiftComboManager sharedManager] resetCombo];
|
||||
NSLog(@"🔴 已启动连击状态");
|
||||
|
||||
// 延迟1秒后模拟网络错误
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
NSLog(@"🔴 模拟网络异常,强制移除连击状态...");
|
||||
[[GiftComboManager sharedManager] forceRemove];
|
||||
});
|
||||
}
|
||||
|
||||
// 启动连击用于测试
|
||||
- (void)startComboForTest {
|
||||
NSLog(@"🟢 [调试] 启动连击用于测试");
|
||||
|
||||
[[GiftComboManager sharedManager] enableToCombo:YES];
|
||||
[[GiftComboManager sharedManager] resetCombo];
|
||||
|
||||
NSLog(@"✅ 连击状态已启动,可以进行异常测试");
|
||||
}
|
||||
|
||||
// 检查当前状态
|
||||
- (void)checkCurrentState {
|
||||
BOOL isCombing = [[GiftComboManager sharedManager] isGiftCombing];
|
||||
BOOL sideMenuHidden = self.sideMenu.hidden;
|
||||
BOOL menuContainerHidden = self.menuContainerView.hidden;
|
||||
|
||||
NSLog(@"📊 [状态检查]");
|
||||
NSLog(@" 连击状态:%@", isCombing ? @"进行中" : @"未进行");
|
||||
NSLog(@" 侧栏状态:%@", sideMenuHidden ? @"隐藏" : @"显示");
|
||||
NSLog(@" 底部栏状态:%@", menuContainerHidden ? @"隐藏" : @"显示");
|
||||
|
||||
if ((sideMenuHidden || menuContainerHidden) && !isCombing) {
|
||||
NSLog(@"⚠️ 检测到状态不一致!");
|
||||
} else {
|
||||
NSLog(@"✅ 状态正常");
|
||||
}
|
||||
}
|
||||
|
||||
// 设置调试按钮
|
||||
- (void)setupDebugButtons {
|
||||
// 创建调试按钮容器
|
||||
UIView *debugContainer = [[UIView alloc] initWithFrame:CGRectMake(20, 80, 280, 260)];
|
||||
debugContainer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.8];
|
||||
debugContainer.layer.cornerRadius = 12;
|
||||
debugContainer.layer.borderWidth = 2;
|
||||
debugContainer.layer.borderColor = [UIColor orangeColor].CGColor;
|
||||
[self.view addSubview:debugContainer];
|
||||
|
||||
// 标题
|
||||
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 5, 280, 25)];
|
||||
titleLabel.text = @"🔧 连击状态调试工具";
|
||||
titleLabel.textColor = [UIColor orangeColor];
|
||||
titleLabel.font = [UIFont boldSystemFontOfSize:16];
|
||||
titleLabel.textAlignment = NSTextAlignmentCenter;
|
||||
[debugContainer addSubview:titleLabel];
|
||||
|
||||
// 创建按钮的通用方法
|
||||
void (^createButton)(NSString *, CGRect, SEL) = ^(NSString *title, CGRect frame, SEL action) {
|
||||
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[button setTitle:title forState:UIControlStateNormal];
|
||||
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
button.backgroundColor = [[UIColor systemBlueColor] colorWithAlphaComponent:0.7];
|
||||
button.layer.cornerRadius = 6;
|
||||
button.frame = frame;
|
||||
button.titleLabel.font = [UIFont systemFontOfSize:12];
|
||||
[button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
|
||||
[debugContainer addSubview:button];
|
||||
};
|
||||
|
||||
// 第一行按钮
|
||||
createButton(@"启动连击", CGRectMake(10, 35, 80, 30), @selector(startComboForTest));
|
||||
createButton(@"检查状态", CGRectMake(100, 35, 80, 30), @selector(checkCurrentState));
|
||||
createButton(@"强制重置", CGRectMake(190, 35, 80, 30), @selector(forceBoomStateReset));
|
||||
|
||||
// 第二行按钮
|
||||
createButton(@"状态不一致", CGRectMake(10, 75, 80, 30), @selector(simulateStateInconsistency));
|
||||
createButton(@"UI消失", CGRectMake(100, 75, 80, 30), @selector(simulateGiftViewUIDisappear));
|
||||
createButton(@"进入后台", CGRectMake(190, 75, 80, 30), @selector(simulateAppEnterBackground));
|
||||
|
||||
// 第三行按钮
|
||||
createButton(@"内存警告", CGRectMake(10, 115, 80, 30), @selector(simulateMemoryWarning));
|
||||
createButton(@"网络异常", CGRectMake(100, 115, 80, 30), @selector(simulateNetworkError));
|
||||
|
||||
// 说明文字
|
||||
UILabel *instructionLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 160, 260, 90)];
|
||||
instructionLabel.text = @"使用步骤:\n1. 点击\"启动连击\"开始测试\n2. 点击各种异常模拟按钮\n3. 观察控制台日志输出\n4. 检查UI状态是否正确恢复\n5. 点击\"检查状态\"验证结果";
|
||||
instructionLabel.textColor = [UIColor lightGrayColor];
|
||||
instructionLabel.font = [UIFont systemFontOfSize:11];
|
||||
instructionLabel.numberOfLines = 0;
|
||||
instructionLabel.textAlignment = NSTextAlignmentLeft;
|
||||
[debugContainer addSubview:instructionLabel];
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - Private Method
|
||||
- (void)initSubViews {
|
||||
self.view.backgroundColor = [UIColor darkGrayColor];
|
||||
|
@@ -422,3 +422,4 @@ if (![partitionId isEqualToString:self.userInfo.partitionId]) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -618,3 +618,4 @@ A: 检查APNS证书配置,确保设备Token正确上传。
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -555,3 +555,4 @@ A: 检查推送消息格式,确保解析逻辑正确。
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user