优化 RoomAnimationView 和 PIGiftBravoGiftBroadcastView 的内存管理,新增动画控制方法以提升用户体验。更新了连击视图的清理逻辑,确保资源的正确释放,避免内存泄漏。同时,新增 NetImageView 的取消加载功能,增强图片加载的灵活性和性能。重构相关方法以支持新的逻辑流程,提升代码可维护性和可读性。

This commit is contained in:
edwinQQQ
2025-08-20 18:07:34 +08:00
parent e0466700b5
commit 0b89480a77
8 changed files with 419 additions and 230 deletions

View File

@@ -1,79 +1,26 @@
-[XPSendGiftView xPGiftBarView:didClickSendGift:] [Line 771][Combo effect] 🎁 开始送礼物流程 -[XPRoomViewController dealloc] [Line 314]🔄 XPRoomViewController: 清理 RoomAnimationView
-[XPSendGiftView readyForCombo:gift:] [Line 727][Combo effect] 🔧 准备连击状态 - giftType: 18, segmentType: 8 -[RoomAnimationView removeItSelf] [Line 193]<5D><> RoomAnimationView: 开始销毁
-[XPSendGiftView readyForCombo:gift:] [Line 745][Combo effect] ✅ 礼物支持连击,启用连击功能 -[RoomAnimationView cleanupAllSubviews] [Line 239] 清理所有子视图
-[GiftComboManager activate] [Line 197][Combo effect] 🔧 激活连击功能 -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftBannerView (从容器: XPRoomAnimationHitView)
-[GiftComboManager configureWithGiftInfo:targetUIDs:roomUID:sessionID:userInfo:countModel:sourceType:sendType:giftNum:] [Line 696][Combo effect] 🔧 统一配置连击参数 -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftWinningFlagView (从容器: XPRoomAnimationHitView)
-[GiftComboManager configureWithGiftInfo:targetUIDs:roomUID:sessionID:userInfo:countModel:sourceType:sendType:giftNum:] [Line 709][Combo effect] ✅ 连击参数配置完成 - giftId: 2263, targetCount: 1 -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftWinningFlagView (从容器: XPRoomAnimationHitView)
-[XPSendGiftView readyForCombo:gift:] [Line 761][Combo effect] ✅ 连击状态准备完成 -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftWinningFlagView (从容器: XPRoomAnimationHitView)
-[GiftComboManager printComboState] [Line 377][Combo effect] 📊 当前连击状态: -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftWinningFlagView (从容器: XPRoomAnimationHitView)
-[GiftComboManager printComboState] [Line 378][Combo effect] - isCombing: NO -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: BravoGiftWinningFlagView (从容器: XPRoomAnimationHitView)
-[GiftComboManager printComboState] [Line 379][Combo effect] - enableCombo: YES -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
-[GiftComboManager printComboState] [Line 380][Combo effect] - combo: 1 -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
-[GiftComboManager printComboState] [Line 381][Combo effect] - hasGiftInfo: YES -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
-[GiftComboManager printComboState] [Line 382][Combo effect] - targetCount: 1 -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
-[GiftComboManager printComboState] [Line 383][Combo effect] - errorMessage: -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
-[XPSendGiftView xPGiftBarView:didClickSendGift:] [Line 778][Combo effect] ✅ 连击功能已启用准备调用resetCombo -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
-[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1198][Combo effect] 📱 检查连击状态 - enableCombo: YES -[RoomAnimationView cleanupAllSubviews] [Line 250]<5D><> 标记移除视图: NetImageView (从容器: XPRoomAnimationHitView)
-[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1207][Combo effect] 📱 originDic 连击计数检查 - comboCount: (null) -[RoomAnimationView cleanupAllSubviews] [Line 265]🔄 所有子视图清理完成
-[XPSendGiftView sendGiftSuccess:originDic:uidCount:] [Line 1210][Combo effect] 📱 启用连击模式,重置连击状态 -[RoomAnimationView removeItSelf] [Line 220]<5D><> 清理 BannerScheduler
-[GiftComboManager reset] [Line 141][Combo effect] 🔄 开始连击重置 - combo: 1 -> 1, enableCombo: YES, actionCallback: 为空 -[RoomAnimationView cleanupGestureRecognizers] [Line 3690]🔄 清理手势识别器
-[GiftComboManager reset] [Line 154][Combo effect] 🔍 重置后验证 - combo: 1 -[RoomAnimationView cleanupGestureRecognizers] [Line 3713]🔄 手势识别器清理完成
-[GiftComboManager reset] [Line 173][Combo effect] ⚠️ actionCallback为空不显示连击面板 -[RoomAnimationView cleanupCacheManagers] [Line 3717]🔄 清理缓存管理器
-[GiftComboManager reset] [Line 177][Combo effect] 🎮 隐藏房间UI元素 -[RoomAnimationView cleanupCacheManagers] [Line 3730]🔄 缓存管理器清理完成
-[GiftComboManager reset] [Line 186][Combo effect] ✅ 连击重置完成 - isCombing: NO -[RoomAnimationView removeNotificationObservers] [Line 3743]🔄 移除通知监听
-[XPSendGiftView sendCustomMessage:oringinDic:] [Line 425][Combo effect] 📨 云信消息连击计数检查 - comboCount: 1, giftId: 2263 -[RoomAnimationView removeNotificationObservers] [Line 3753]🔄 通知监听移除完成
-[GiftComboView setupTimer] [Line 284][Combo effect] ⏰ 设置连击倒计时 -[RoomAnimationView removeItSelf] [Line 235]<5D><> RoomAnimationView: 销毁完成
-[CountdownRingView startCountdown] [Line 97][Combo effect] ⏰ 开始倒计时 -[RoomAnimationView playBroveBanner:]_block_invoke [Line 752]🔄 BravoGiftBannerView complete 回调被调用
-[CountdownRingView animateRing] [Line 246][Combo effect] 🎬 环形动画已启动,时长: 5.0秒
-[CountdownRingView startCountdown] [Line 109][Combo effect] ⏰ 倒计时已启动
-[GiftComboView setupTimer] [Line 293][Combo effect] ⏰ 连击倒计时已启动
-[GiftComboView updateCount] [Line 171][Combo effect] 🔢 更新连击次数显示 - combo: 1
-[GiftComboManager printComboState] [Line 377][Combo effect] 📊 当前连击状态:
-[GiftComboManager printComboState] [Line 378][Combo effect] - isCombing: NO
-[GiftComboManager printComboState] [Line 379][Combo effect] - enableCombo: YES
-[GiftComboManager printComboState] [Line 380][Combo effect] - combo: 1
-[GiftComboManager printComboState] [Line 381][Combo effect] - hasGiftInfo: YES
-[GiftComboManager printComboState] [Line 382][Combo effect] - targetCount: 1
-[GiftComboManager printComboState] [Line 383][Combo effect] - errorMessage:
-[GiftComboManager forceBoomStateReset] [Line 275][Combo effect] 🚨 执行强制Boom连击状态重置
-[GiftComboManager forceBoomStateReset] [Line 278][Combo effect] ⏰ 停止所有定时器
-[GiftComboManager forceStopAllTimers] [Line 321][Combo effect] ⏰ 强制停止所有Timer
-[GiftComboManager forceBoomStateReset] [Line 282][Combo effect] 🗑️ 清空所有队列
-[GiftComboManager forceBoomStateReset] [Line 287][Combo effect] 🔄 重置状态标志 - isCombing: NO -> NO
-[GiftComboManager forceBoomStateReset] [Line 292][Combo effect] 🔄 combo计数重置为0
-[GiftComboManager forceBoomStateReset] [Line 301][Combo effect] 📢 发送强制重置通知
-[GiftComboManager forceBoomStateReset] [Line 309][Combo effect] 🎮 恢复房间UI元素
-[GiftComboManager forceBoomStateReset] [Line 316][Combo effect] ✅ 强制重置完成 - enableCombo保持: YES, actionCallback保持: 为空
-[XPSendGiftView removeAllComboRelatedViews] [Line 137][Combo effect] 🗑️ 开始移除连击相关视图
-[XPSendGiftView removeAllComboRelatedViews] [Line 148][Combo effect] 🗑️ comboView存在但无superview直接清理
-[CountdownRingView stopCountdown] [Line 200][Combo effect] ⏰ 停止倒计时开始
-[CountdownRingView stopCountdown] [Line 208][Combo effect] ⏰ Timer已停止
-[CountdownRingView stopCountdown] [Line 214][Combo effect] ⏰ 动画已停止
-[CountdownRingView stopCountdown] [Line 230][Combo effect] ⏰ 停止倒计时完成
-[GiftComboView dealloc] [Line 43][Combo effect] 🗑️ GiftComboView dealloc开始 - 0x13624e600
-[CountdownRingView stopCountdown] [Line 196][Combo effect] ⚠️ 倒计时未运行,无需停止
-[GiftComboView dealloc] [Line 72][Combo effect] 🗑️ GiftComboView dealloc完成 - 0x13624e600
-[XPSendGiftView removeAllComboRelatedViews] [Line 166][Combo effect] 🗑️ 连击相关视图移除完成
-[CountdownRingView dealloc] [Line 32][Combo effect] 🗑️ CountdownRingView dealloc开始 - 0x13624e800
-[CountdownRingView cleanup] [Line 251][Combo effect] 🗑️ 完全清理开始
-[CountdownRingView stopCountdown] [Line 196][Combo effect] ⚠️ 倒计时未运行,无需停止
-[CountdownRingView cleanup] [Line 260][Combo effect] 🗑️ 完全清理完成
-[CountdownRingView dealloc] [Line 37][Combo effect] 🗑️ CountdownRingView dealloc完成 - 0x13624e800
-[RoomAnimationView _handleGiftMessage:] [Line 1737][Combo effect] 📨 收到礼物消息 - second: 121
-[RoomAnimationView _handleGiftMessage:] [Line 1757][Combo effect] 📨 礼物消息解析完成 - giftId: 2263, combo: 1, uid: 3184
-[RoomAnimationView _handleGiftMessage:] [Line 1824][Combo effect] 🎁 处理普通礼物动画
-[GiftAnimationManager enqueueGift:] [Line 237][Combo effect] 🎬 添加礼物到动画队列 - giftId: 2263, combo: 1
-[GiftComboManager receiveGiftInfoForDisplayComboFlags:container:] [Line 407][Combo effect] 🎪 收到连击飘屏请求 - combo: 1, giftId: 2263
-[GiftAnimationManager enqueueGift:]_block_invoke [Line 241][Combo effect] 📊 动画队列当前数量: 1
-[GiftComboManager receiveGiftInfoForDisplayComboFlags:container:] [Line 410][Combo effect] 📊 连击飘屏队列数量: 1
-[GiftComboManager processGiftFlagQueue] [Line 462][Combo effect] 🎪 处理连击飘屏 - combo: 1, giftId: 2263
-[GiftComboManager processGiftFlagQueue] [Line 464][Combo effect] <20><> 移除后UI动画队列数量: 0
-[GiftAnimationManager processNextGift]_block_invoke [Line 100][Combo effect] 🎬 开始处理动画 - giftId: 2263, combo: 1
-[GiftAnimationManager processNextGift]_block_invoke [Line 104][Combo effect] 📊 移除后动画队列数量: 0
-[GiftAnimationManager distributeGiftAnimation:] [Line 119][Combo effect] 🎯 开始分发动画 - uid: 3184
-[GiftAnimationManager distributeGiftAnimation:] [Line 122][Combo effect] 🎯 目标用户数量: 1
-[GiftAnimationManager distributeGiftAnimation:] [Line 126][Combo effect] 🎯 是否使用连击动画: NO
-[GiftAnimationManager distributeGiftAnimation:] [Line 133][Combo effect] 🎯 使用普通动画起点: {207, 285.79999999999995}
-[GiftAnimationManager distributeGiftAnimation:] [Line 136][Combo effect] 🎯 动画延迟时间: 0.30
-[GiftAnimationManager distributeGiftAnimation:] [Line 140][Combo effect] 🎯 为目标用户 3184 创建动画 - 终点: {207, 285.79999999999995}
-[GiftAnimationManager processNextGift]_block_invoke [Line 82][Combo effect] 📭 动画队列为空,停止处理

View File

@@ -37,6 +37,9 @@ typedef NS_ENUM(NSInteger, NetImageState){
- (void)loadImageWithUrl:(NSString * _Nonnull)imageUrl completion:(LoadCompletion _Nullable)completion fail:(LoadFail _Nullable)fail; - (void)loadImageWithUrl:(NSString * _Nonnull)imageUrl completion:(LoadCompletion _Nullable)completion fail:(LoadFail _Nullable)fail;
- (void)updateConfigPlaceHolder:(UIImage *)image; - (void)updateConfigPlaceHolder:(UIImage *)image;
- (void)cancelLoadImage;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -43,6 +43,10 @@
return self; return self;
} }
- (void)cancelLoadImage {
[self sd_cancelCurrentImageLoad];
}
- (void)initImageUrl:(NSString *)imageUrl { - (void)initImageUrl:(NSString *)imageUrl {
_imageUrl = imageUrl; _imageUrl = imageUrl;
_innerConfigUrl = [UIImageConstant configUrl:_imageUrl type:self.config.imageType radius:self.config.radius]; _innerConfigUrl = [UIImageConstant configUrl:_imageUrl type:self.config.imageType radius:self.config.radius];

View File

@@ -186,50 +186,145 @@ BannerSchedulerDelegate
- (void)dealloc - (void)dealloc
{ {
NSLog(@" RoomAnimationView: dealloc 开始");
[self removeItSelf];
NSLog(@" RoomAnimationView: dealloc 完成");
} }
- (void)removeItSelf { - (void)removeItSelf {
NSLog(@"<22><> RoomAnimationView: 开始销毁"); NSLog(@"<22><> RoomAnimationView: 开始销毁");
// 🔧
[self cancelAllAnimations];
// 广 // 广
[[NIMSDK sharedSDK].broadcastManager removeDelegate:self]; [[NIMSDK sharedSDK].broadcastManager removeDelegate:self];
//
[NSObject cancelPreviousPerformRequestsWithTarget:self];
// POP
[self pop_removeAllAnimations];
//
if (_giftEffectTimer && !dispatch_source_testcancel(_giftEffectTimer)) {
dispatch_source_cancel(_giftEffectTimer);
_giftEffectTimer = nil;
}
//
if (_giftEffectsQueue) {
self.giftEffectsQueue = NULL;
}
// <EFBFBD><EFBFBD> banner block
[self cleanupAllSubviews];
// 🔧 BannerScheduler
if (self.bannerScheduler) {
NSLog(@"<22><> 清理 BannerScheduler");
[self.bannerScheduler clearQueue];
[self.bannerScheduler pause];
self.bannerScheduler = nil;
}
//
[self cleanupGestureRecognizers];
//
[self cleanupCacheManagers];
//
[self removeNotificationObservers];
NSLog(@"<22><> RoomAnimationView: 销毁完成");
}
- (void)cancelAllAnimations {
NSLog(@" 取消所有动画");
// // POP
[NSObject cancelPreviousPerformRequestsWithTarget:self];
// POP
[self pop_removeAllAnimations]; [self pop_removeAllAnimations];
// // POP
if (_giftEffectTimer && !dispatch_source_testcancel(_giftEffectTimer)) { NSArray *containers = @[self.bannerContainer, self.topContainer, self.middleContainer, self.bottomContainer];
dispatch_source_cancel(_giftEffectTimer); for (UIView *container in containers) {
_giftEffectTimer = nil; if (!container) continue;
for (UIView *subview in container.subviews) {
[subview pop_removeAllAnimations];
}
} }
// // UIView
if (_giftEffectsQueue) { [UIView setAnimationsEnabled:NO];
self.giftEffectsQueue = NULL;
NSLog(@"🔄 所有动画取消完成");
}
- (void)cleanupAllSubviews {
NSLog(@" 清理所有子视图");
//
NSArray *containers = @[self.bannerContainer, self.topContainer, self.middleContainer, self.bottomContainer];
for (UIView *container in containers) {
if (!container) continue;
NSMutableArray *viewsToRemove = [NSMutableArray array];
for (UIView *subview in container.subviews) {
[viewsToRemove addObject:subview];
NSLog(@" 标记移除视图: %@ (从容器: %@)", NSStringFromClass([subview class]), NSStringFromClass([container class]));
}
for (UIView *view in viewsToRemove) {
//
[view pop_removeAllAnimations];
//
if (view.gestureRecognizers.count > 0) {
NSLog(@" 清理视图 %@ 上的 %lu 个手势识别器", NSStringFromClass([view class]), (unsigned long)view.gestureRecognizers.count);
for (UIGestureRecognizer *gesture in view.gestureRecognizers.copy) {
[view removeGestureRecognizer:gesture];
}
}
//
[view removeFromSuperview];
}
} }
// 🔧 BannerScheduler NSLog(@"🔄 所有子视图清理完成");
if (self.bannerScheduler) { }
NSLog(@"<22><> 清理 BannerScheduler");
[self.bannerScheduler clearQueue]; - (void)__cleanupAllBannerViews {
[self.bannerScheduler pause]; NSLog(@"<22><> 清理所有 Banner 视图");
self.bannerScheduler = nil;
// bannerContainer
NSMutableArray *viewsToRemove = [NSMutableArray array];
for (UIView *subview in self.bannerContainer.subviews) {
[viewsToRemove addObject:subview];
NSLog(@"🔄 标记移除 banner: %@", NSStringFromClass([subview class]));
} }
// for (UIView *view in viewsToRemove) {
[self cleanupGestureRecognizers]; [view removeFromSuperview];
}
// // topContainer CP banner
[self cleanupCacheManagers]; viewsToRemove = [NSMutableArray array];
for (UIView *subview in self.topContainer.subviews) {
if ([subview isKindOfClass:[CPBindingAnimation class]] ||
[subview isKindOfClass:[CPLevelUpAnimation class]]) {
[viewsToRemove addObject:subview];
NSLog(@"<22><> 标记移除 CP banner: %@", NSStringFromClass([subview class]));
}
}
// for (UIView *view in viewsToRemove) {
[self removeNotificationObservers]; [view removeFromSuperview];
}
NSLog(@"<EFBFBD><EFBFBD> RoomAnimationView: 销毁完成"); NSLog(@"🔄 Banner 视图清理完成");
} }
-(void)resumeTimer{ -(void)resumeTimer{
@@ -541,7 +636,7 @@ BannerSchedulerDelegate
return; return;
} }
#if DEBUG #if 0
// DEBUGbanner // DEBUGbanner
BOOL enableBannerCopy = obj.second == Custom_Message_Sub_Super_Gift_Banner;// YES; // NO BOOL enableBannerCopy = obj.second == Custom_Message_Sub_Super_Gift_Banner;// YES; // NO
NSInteger copyCount = 10; // NSInteger copyCount = 10; //
@@ -669,25 +764,40 @@ BannerSchedulerDelegate
- (void)playBroveBanner:(AttachmentModel *)obj { - (void)playBroveBanner:(AttachmentModel *)obj {
if (!obj.data) { if (!obj.data) {
self.isRoomBannerV2Displaying = NO; self.isRoomBannerV2Displaying = NO;
[self.bannerScheduler markBannerFinished]; [self.bannerScheduler markBannerFinished];
return; return;
} }
self.isRoomBannerV2Displaying = YES; self.isRoomBannerV2Displaying = YES;
@kWeakify(self); @kWeakify(self);
RoomInfoModel *roomInfo = self.hostDelegate.getRoomInfo; RoomInfoModel *roomInfo = self.hostDelegate.getRoomInfo;
[BravoGiftBannerView display:self.bannerContainer
inRoomUid:roomInfo.uid // <EFBFBD><EFBFBD>
with:obj if (!self || !self.superview) {
complete:^{ NSLog(@"⚠️ RoomAnimationView 正在销毁,跳过 banner 播放");
@kStrongify(self); return;
NSLog(@"🔄 BravoGiftBannerView complete 回调被调用"); }
self.isRoomBannerV2Displaying = NO;
[self.bannerScheduler markBannerFinished]; [BravoGiftBannerView display:self.bannerContainer
} exitCurrentRoom:^{ inRoomUid:roomInfo.uid
@kStrongify(self); with:obj
[self.hostDelegate exitRoom]; complete:^{
}]; @kStrongify(self);
if (!self || !self.superview) {
NSLog(@"⚠️ RoomAnimationView 已被释放,跳过 complete 回调");
return;
}
NSLog(@"🔄 BravoGiftBannerView complete 回调被调用");
self.isRoomBannerV2Displaying = NO;
[self.bannerScheduler markBannerFinished];
} exitCurrentRoom:^{
@kStrongify(self);
if (!self || !self.superview) {
NSLog(@"⚠️ RoomAnimationView 已被释放,跳过 exitCurrentRoom 回调");
return;
}
[self.hostDelegate exitRoom];
}];
} }
- (void)playLuckyPackageBanner:(AttachmentModel *)obj { - (void)playLuckyPackageBanner:(AttachmentModel *)obj {

View File

@@ -11,6 +11,13 @@ NS_ASSUME_NONNULL_BEGIN
@interface PIGiftBravoGiftBroadcastView : UIView @interface PIGiftBravoGiftBroadcastView : UIView
// 开始动画循环
- (void)startAnimation;
// 停止动画循环
- (void)stopAnimation;
// 兼容旧方法
- (void)endloop; - (void)endloop;
@end @end

View File

@@ -8,24 +8,123 @@
#import "PIGiftBravoGiftBroadcastView.h" #import "PIGiftBravoGiftBroadcastView.h"
#import "XPSkillCardPlayerManager.h" #import "XPSkillCardPlayerManager.h"
#import "BravoGiftTabInfomationModel.h" #import "BravoGiftTabInfomationModel.h"
@interface PIGiftBravoGiftBroadcastItemView : UIView
@property (nonatomic, strong) BravoGiftTabInfomationModel *model;
- (void)cancelLoading;
@end
@implementation PIGiftBravoGiftBroadcastItemView
{
NetImageView *avatar;
UILabel *content;
}
- (instancetype)init
{
self = [super init];
if (self) {
NetImageConfig *config = [[NetImageConfig alloc] init];
config.placeHolder = [UIImageConstant defaultAvatarPlaceholder];
config.imageType = ImageTypeUserIcon;
avatar = [[NetImageView alloc] initWithConfig:config];
content = [[UILabel alloc] init];
[self addSubview:avatar];
[self addSubview:content];
[avatar mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self);
make.leading.mas_equalTo(8);
make.size.mas_equalTo(CGSizeMake(24, 24));
}];
[content mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(self);
make.leading.mas_equalTo(avatar.mas_trailing).offset(4);
make.trailing.mas_equalTo(self).offset(-4);
}];
}
return self;
}
- (void)setModel:(BravoGiftTabInfomationModel *)model {
_model = model;
avatar.imageUrl = model.avatar;
NSTextAttachment *coinAttachment = [[NSTextAttachment alloc] init];
coinAttachment.image = kImage(@"moli_money_icon");
coinAttachment.bounds = CGRectMake(0, -3, 18, 18);
NSAttributedString *coinString = [NSAttributedString attributedStringWithAttachment:coinAttachment];
NSString *giftName = model.giftName;
NSString *coin = @(model.coin).stringValue;
NSString *oringinalString = [NSString stringWithFormat:@" %@ %@ %@ %@ %@ ",
[NSString trimString:model.nick lengthLimit:4],
YMLocalizedString(@"Combo_7"),
giftName, YMLocalizedString(@"Combo_4"), coin];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] init];
[string appendAttributedString:[[NSAttributedString alloc] initWithString:oringinalString attributes:@{
NSFontAttributeName: kFontRegular(13),
NSForegroundColorAttributeName: [UIColor whiteColor]
}]];
// giftName coin
NSRange giftNameRange = [oringinalString rangeOfString:giftName];
NSRange coinRange = [oringinalString rangeOfString:coin];
//
NSDictionary *yellowAttributes_name = @{
NSFontAttributeName: kFontRegular(13),
NSForegroundColorAttributeName: UIColorFromRGB(0xffec6f)
};
NSDictionary *yellowAttributes_coin = @{
NSFontAttributeName: kFontSemibold(16),
NSForegroundColorAttributeName: UIColorFromRGB(0xffec6f)
};
//
NSMutableAttributedString *yellowGiftName = [[NSMutableAttributedString alloc] initWithString:giftName attributes:yellowAttributes_name];
NSMutableAttributedString *yellowCoin = [[NSMutableAttributedString alloc] initWithString:coin attributes:yellowAttributes_coin];
// giftName coin
[string replaceCharactersInRange:giftNameRange withAttributedString:yellowGiftName];
[string replaceCharactersInRange:coinRange withAttributedString:yellowCoin];
[string appendAttributedString:coinString];
[content setAttributedText:string.copy];
}
- (void)cancelLoading {
[avatar cancelLoadImage];
}
@end
@interface PIGiftBravoGiftBroadcastView() @interface PIGiftBravoGiftBroadcastView()
@property (nonatomic, strong) UIImageView *speaker; @property (nonatomic, strong) UIImageView *speaker;
@property (nonatomic, strong) MarqueeLabel *label; @property (nonatomic, strong) MarqueeLabel *label;
@property (nonatomic, strong) UIView *container; @property (nonatomic, strong) UIView *container;
@property (nonatomic, copy) NSArray <BravoGiftTabInfomationModel *>* source; @property (nonatomic, copy) NSArray <BravoGiftTabInfomationModel *>* source;
@property (nonatomic, strong) NSMutableArray *labels; @property (nonatomic, strong) NSMutableArray<PIGiftBravoGiftBroadcastItemView *> *labels;
@property (nonatomic, assign) NSInteger index; @property (nonatomic, assign) NSInteger index;
@property (nonatomic, assign) NSInteger loopStatus; // -1: 0:1: @property (nonatomic, assign) BOOL isAnimating; //
@property (nonatomic, strong) NSMutableArray *imageLoaders; @property (nonatomic, assign) BOOL shouldStopAnimation; //
@property (nonatomic, strong) UIButton *tipsButton; @property (nonatomic, strong) UIButton *tipsButton;
@property (nonatomic, strong) NSTimer *animationTimer; //
@end @end
@implementation PIGiftBravoGiftBroadcastView @implementation PIGiftBravoGiftBroadcastView
- (void)dealloc { - (void)dealloc {
[self endloop];
} }
- (instancetype)initWithFrame:(CGRect)frame { - (instancetype)initWithFrame:(CGRect)frame {
@@ -59,124 +158,142 @@
}]; }];
self.labels = [[NSMutableArray alloc] init]; self.labels = [[NSMutableArray alloc] init];
self.imageLoaders = [[NSMutableArray alloc] init]; self.isAnimating = NO;
self.shouldStopAnimation = NO;
@kWeakify(self); @kWeakify(self);
[self.source enumerateObjectsUsingBlock:^(BravoGiftTabInfomationModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [self.source enumerateObjectsUsingBlock:^(BravoGiftTabInfomationModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
@kStrongify(self); @kStrongify(self);
NetImageView *avatar = [[NetImageView alloc] init]; PIGiftBravoGiftBroadcastItemView *item = [[PIGiftBravoGiftBroadcastItemView alloc] init];
[avatar loadImageWithUrl:obj.avatar completion:^(UIImage * _Nullable image, NSURL * _Nonnull url) { item.frame = CGRectMake(0, 0, KScreenWidth - 25 - 42, 26);
NSTextAttachment *avatarAttachment = [[NSTextAttachment alloc] init]; item.model = obj;
avatarAttachment.image = [image roundedImageWithCornerRadius:12*3 size:CGSizeMake(24*3, 24*3)]; [self.labels addObject:item];
avatarAttachment.bounds = CGRectMake(0, -5, 24, 24);
NSTextAttachment *coinAttachment = [[NSTextAttachment alloc] init];
coinAttachment.image = kImage(@"moli_money_icon");
coinAttachment.bounds = CGRectMake(0, -3, 18, 18);
NSAttributedString *avatarString = [NSAttributedString attributedStringWithAttachment:avatarAttachment];
NSAttributedString *coinString = [NSAttributedString attributedStringWithAttachment:coinAttachment];
NSString *giftName = obj.giftName;
NSString *coin = @(obj.coin).stringValue;
NSString *oringinalString = [NSString stringWithFormat:@" %@ %@ %@ %@ %@ ",
[NSString trimString:obj.nick lengthLimit:4],
YMLocalizedString(@"Combo_7"),
giftName, YMLocalizedString(@"Combo_4"), coin];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] init];
[string appendAttributedString:[[NSAttributedString alloc] initWithString:oringinalString attributes:@{
NSFontAttributeName: kFontRegular(13),
NSForegroundColorAttributeName: [UIColor whiteColor]
}]];
// giftName coin
NSRange giftNameRange = [oringinalString rangeOfString:giftName];
NSRange coinRange = [oringinalString rangeOfString:coin];
//
NSDictionary *yellowAttributes_name = @{
NSFontAttributeName: kFontRegular(13),
NSForegroundColorAttributeName: UIColorFromRGB(0xffec6f)
};
NSDictionary *yellowAttributes_coin = @{
NSFontAttributeName: kFontSemibold(16),
NSForegroundColorAttributeName: UIColorFromRGB(0xffec6f)
};
//
NSMutableAttributedString *yellowGiftName = [[NSMutableAttributedString alloc] initWithString:giftName attributes:yellowAttributes_name];
NSMutableAttributedString *yellowCoin = [[NSMutableAttributedString alloc] initWithString:coin attributes:yellowAttributes_coin];
// giftName coin
[string replaceCharactersInRange:giftNameRange withAttributedString:yellowGiftName];
[string replaceCharactersInRange:coinRange withAttributedString:yellowCoin];
[string insertAttributedString:avatarString atIndex:0];
[string appendAttributedString:coinString];
UILabel *label = [[UILabel alloc] init];
label.attributedText = string.copy;
[self.labels addObject:label];
if (self.loopStatus>-1) {
[self startLoop];
}
}];
[self.imageLoaders addObject:avatar];
}]; }];
//
[self startLoop];
} }
- (void)startLoop { - (void)startLoop {
if (self.loopStatus > 0 || self.labels.count == 0) { if (self.isAnimating || self.labels.count == 0 || self.shouldStopAnimation) {
return; return;
} }
self.loopStatus = 1;
// self.isAnimating = YES;
UILabel *label = [self.labels xpSafeObjectAtIndex:self.index]; [self playCurrentAnimation];
if (label) { }
//
label.frame = CGRectMake(KScreenWidth, 2, KScreenWidth - 25 - 42, 26); //
// - (void)startAnimation {
[self.container addSubview:label]; if (self.shouldStopAnimation) {
self.shouldStopAnimation = NO;
// }
[UIView animateWithDuration:0.5 animations:^{
label.frame = CGRectMake(25, 2, KScreenWidth - 25 - 42, 26); if (self.labels.count > 0) {
} completion:^(BOOL finished) { [self startLoop];
//
[UIView animateWithDuration:0.5 delay:2 options:UIViewAnimationOptionCurveEaseIn animations:^{
label.frame = CGRectMake(-KScreenWidth, 2, KScreenWidth - 25 - 42, 26);
} completion:^(BOOL finished) {
//
[label removeFromSuperview];
//
self.index += 1;
//
if (self.index >= self.labels.count) {
// 0
self.index = 0;
}
// NO
self.loopStatus = 0;
// startLoop
[self startLoop];
}];
}];
} }
} }
//
- (void)stopAnimation {
[self endloop];
}
- (void)playCurrentAnimation {
if (self.shouldStopAnimation) {
self.isAnimating = NO;
return;
}
//
PIGiftBravoGiftBroadcastItemView *item = [self.labels xpSafeObjectAtIndex:self.index];
if (!item) {
self.isAnimating = NO;
return;
}
//
item.frame = CGRectMake(KScreenWidth, 2, KScreenWidth - 25 - 42, 26);
//
[self.container addSubview:item];
@kWeakify(self);
// 0.5
[UIView animateWithDuration:0.5 animations:^{
item.frame = CGRectMake(25, 2, KScreenWidth - 25 - 42, 26);
} completion:^(BOOL finished) {
@kStrongify(self);
if (!finished || self.shouldStopAnimation) {
[item removeFromSuperview];
self.isAnimating = NO;
return;
}
// 2
[UIView animateWithDuration:0.5 delay:2.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
item.frame = CGRectMake(-KScreenWidth, 2, KScreenWidth - 25 - 42, 26);
} completion:^(BOOL finished) {
@kStrongify(self);
//
[item removeFromSuperview];
if (self.shouldStopAnimation) {
self.isAnimating = NO;
return;
}
//
self.index += 1;
if (self.index >= self.labels.count) {
self.index = 0;
}
// 使
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(scheduleNextAnimation) userInfo:nil repeats:NO];
}];
}];
}
- (void)scheduleNextAnimation {
[self.animationTimer invalidate];
self.animationTimer = nil;
if (self.shouldStopAnimation) {
self.isAnimating = NO;
return;
}
[self playCurrentAnimation];
}
- (void)endloop { - (void)endloop {
self.loopStatus = -1; //
self.shouldStopAnimation = YES;
self.isAnimating = NO;
//
if (self.animationTimer) {
[self.animationTimer invalidate];
self.animationTimer = nil;
}
// //
[self.container.layer removeAllAnimations]; [self.container.layer removeAllAnimations];
// //
[self.imageLoaders removeAllObjects]; for (PIGiftBravoGiftBroadcastItemView *item in self.labels) {
[item cancelLoading];
}
// //
for (UILabel *label in self.labels) { for (PIGiftBravoGiftBroadcastItemView *item in self.labels) {
[label removeFromSuperview]; [item.layer removeAllAnimations];
[item removeFromSuperview];
} }
[self.labels removeAllObjects]; [self.labels removeAllObjects];
//
self.index = 0;
} }
- (void)didTapTips { - (void)didTapTips {

View File

@@ -124,12 +124,10 @@ UIKIT_EXTERN NSString * const kFreeGiftCountdownNotification;
[[GiftComboManager sharedManager] registerActions:nil]; [[GiftComboManager sharedManager] registerActions:nil];
if (_bravoGiftView) { if (_bravoGiftView) {
[self.bravoGiftView endloop]; [_bravoGiftView endloop];
} }
} }
#pragma mark - #pragma mark -
// //

View File

@@ -95,7 +95,7 @@
[regularList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [regularList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
@kStrongify(self); @kStrongify(self);
NIMChatroomMember * member = obj; NIMChatroomMember * member = obj;
if (member.type == NIMTeamMemberTypeOwner) { if (member.type == NIMChatroomMemberTypeCreator) {
[self.datasource insertObject:member atIndex:0]; [self.datasource insertObject:member atIndex:0];
}else { }else {
[self.datasource addObject:member]; [self.datasource addObject:member];
@@ -129,7 +129,7 @@
NSArray * regularList = x; NSArray * regularList = x;
[regularList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [regularList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NIMChatroomMember * member = obj; NIMChatroomMember * member = obj;
if (member.type == NIMTeamMemberTypeOwner) { if (member.type == NIMChatroomMemberTypeCreator) {
[self.datasource insertObject:member atIndex:0]; [self.datasource insertObject:member atIndex:0];
}else { }else {
[self.datasource addObject:member]; [self.datasource addObject:member];
@@ -326,6 +326,9 @@
} }
- (void)handleUpMicAction:(NIMChatroomMember *)member { - (void)handleUpMicAction:(NIMChatroomMember *)member {
// TODO 31
if ([AccountInfoStorage instance].getUid.integerValue == member.userId.integerValue) { if ([AccountInfoStorage instance].getUid.integerValue == member.userId.integerValue) {
[Api getUserInfo:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { [Api getUserInfo:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
if (code == 200) { if (code == 200) {