This commit is contained in:
启星
2025-10-20 20:05:55 +08:00
parent fdeed970d4
commit 0e20842c69
714 changed files with 5210 additions and 78 deletions

View File

@@ -0,0 +1,16 @@
//
// QXRoomViewController+QXGiftDrift.h
// QXLive
//
// Created by 启星 on 2025/10/20.
//
#import "QXRoomViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface QXRoomViewController (QXGiftDrift)
-(void)initGiftDriftManager;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,16 @@
//
// QXRoomViewController+QXGiftDrift.m
// QXLive
//
// Created by on 2025/10/20.
//
#import "QXRoomViewController+QXGiftDrift.h"
#import "QXGiftDisplayManager.h"
@implementation QXRoomViewController (QXGiftDrift)
-(void)initGiftDriftManager{
//
[[QXGiftDisplayManager sharedManager] setupDisplayViewInContainer:self.view];
}
@end

View File

@@ -33,6 +33,7 @@
#import "QXRedBagSendView.h"
#import "QXRoomViewController+RedPacket.h"
#import "QXRoomViewController+Activity.h"
#import "QXRoomViewController+QXGiftDrift.h"
#import "QXRedBagRecordViewController.h"
@interface QXRoomViewController ()<
@@ -90,6 +91,8 @@ QXRoomUserInfoViewDelegate
[super viewDidLoad];
// Do any additional setup after loading the view.
[self initRedpacketManager];
[self initGiftDriftManager];
[self initActivityObeserver];
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
@@ -133,7 +136,7 @@ QXRoomUserInfoViewDelegate
-(void)initSubViews{
// [self updateBgImage:@"room_background"];
[self initActivityObeserver];
self.bgImageHidden = YES;
self.roomBgImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
// self.roomBgImageView.image = [UIImage imageNamed:@"room_background"];

View File

@@ -94,5 +94,9 @@ typedef NS_ENUM(NSInteger) {
/// 礼物数量 巡乐会
@property (nonatomic,strong)NSString *gift_num;
- (BOOL)isSameGiftFromSameSender:(QXRoomChatListModel *)otherGift;
- (instancetype)copy;
@end
NS_ASSUME_NONNULL_END

View File

@@ -920,4 +920,30 @@ NSInteger maxMessageCount = 20;
};
}
- (BOOL)isSameGiftFromSameSender:(QXRoomChatListModel *)otherGift {
if (!otherGift) return NO;
return [self.GiftInfo.gift_id isEqualToString:otherGift.GiftInfo.gift_id] &&
[self.FromUserInfo.nickname isEqualToString:otherGift.FromUserInfo.nickname] &&
[self.ToUserInfo.nickname isEqualToString:otherGift.ToUserInfo.nickname];
}
- (instancetype)copy {
QXRoomChatListModel *copy = [[QXRoomChatListModel alloc] init];
QXGiftModel *gift = [[QXGiftModel alloc] init];
gift.gift_id = self.GiftInfo.gift_id;
gift.base_image = self.GiftInfo.base_image;
gift.gift_name = self.GiftInfo.gift_name;
copy.GiftInfo = gift;
QXUserHomeModel *fromUser = [[QXUserHomeModel alloc] init];
fromUser.nickname = self.FromUserInfo.nickname;
fromUser.avatar = self.FromUserInfo.avatar;
copy.FromUserInfo = fromUser;
QXUserHomeModel *toUser = [[QXUserHomeModel alloc] init];
toUser.nickname = self.FromUserInfo.nickname;
toUser.avatar = self.FromUserInfo.avatar;
copy.ToUserInfo = toUser;
copy.gift_num = self.gift_num;
return copy;
}
@end

View File

@@ -0,0 +1,23 @@
//
// QXGiftDisplayManager.h
// QXLive
//
// Created by 启星 on 2025/10/20.
//
#import <Foundation/Foundation.h>
#import "QXGiftScrollView.h"
@interface QXGiftDisplayManager : NSObject
+ (instancetype)sharedManager;
// 接收礼物
- (void)receiveGift:(QXRoomChatListModel *)gift;
// 设置飘屏容器视图
- (void)setupDisplayViewInContainer:(UIView *)container;
- (void)clearAll;
@end

View File

@@ -0,0 +1,237 @@
//
// QXGiftDisplayManager.m
// Test
//
// Created by on 2025/10/20.
//
// QXGiftDisplayManager.m
#import "QXGiftDisplayManager.h"
#import "QXGiftDisplayView.h"
@interface QXGiftDisplayManager () <QXGiftDisplayViewDelegate>
@property (nonatomic, weak) UIView *containerView;
@property (nonatomic, strong) NSMutableArray<QXGiftDisplayView *> *displayViews;
@property (nonatomic, strong) NSMutableArray<QXRoomChatListModel *> *giftQueue;
@property (nonatomic, strong) NSMutableDictionary *accumulatedGifts;
@property (nonatomic, strong) NSMutableDictionary *waitingUpdateGifts; //
@property (nonatomic, assign) BOOL isProcessingQueue; //
@end
@implementation QXGiftDisplayManager
+ (instancetype)sharedManager {
static QXGiftDisplayManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
instance = [[QXGiftDisplayManager alloc] init];
});
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
_displayViews = [NSMutableArray array];
_giftQueue = [NSMutableArray array];
_accumulatedGifts = [NSMutableDictionary dictionary];
_waitingUpdateGifts = [NSMutableDictionary dictionary];
_isProcessingQueue = NO;
}
return self;
}
- (void)setupDisplayViewInContainer:(UIView *)container {
self.containerView = container;
[self createDisplayViews];
}
- (void)createDisplayViews {
if (self.displayViews.count > 0) return;
CGFloat viewHeight = 40;
CGFloat spacing = 10;
CGFloat topMargin = 100;
CGFloat width = 270;
for (int i = 0; i < 3; i++) {
CGFloat y = topMargin + (viewHeight + spacing) * i;
CGRect frame = CGRectMake(0, y, width, viewHeight);
QXGiftDisplayView *displayView = [[QXGiftDisplayView alloc] initWithFrame:frame];
displayView.delegate = self;
displayView.tag = 1000 + i; // tag便
displayView.alpha = 0.0;
[self.containerView addSubview:displayView];
[self.displayViews addObject:displayView];
NSLog(@"创建飘屏视图 %d", i);
}
}
- (void)receiveGift:(QXRoomChatListModel *)gift {
if (!gift) return;
dispatch_async(dispatch_get_main_queue(), ^{
[self internalReceiveGift:gift];
});
}
- (void)internalReceiveGift:(QXRoomChatListModel *)gift {
//
QXGiftDisplayView *displayingView = [self findDisplayingViewForGift:gift];
if (displayingView) {
//
NSString *key = [self giftKeyForGift:gift];
QXRoomChatListModel *accumulatedGift = self.accumulatedGifts[key];
if (accumulatedGift) {
NSInteger gift_num = accumulatedGift.gift_num.integerValue;
gift_num += gift.gift_num.integerValue;
accumulatedGift.gift_num = [NSString stringWithFormat:@"%ld",gift_num];
[displayingView updateGiftCount:accumulatedGift.gift_num.integerValue];
NSLog(@"礼物累加: %@ x%@", gift.GiftInfo.gift_name, accumulatedGift.gift_num);
}
} else {
//
QXGiftDisplayView *availableView = [self findAvailableDisplayView];
if (availableView) {
//
NSString *key = [self giftKeyForGift:gift];
self.accumulatedGifts[key] = [gift copy];
[availableView showGift:gift];
NSLog(@"立即显示礼物在视图 %ld", (long)availableView.tag);
} else {
//
[self.giftQueue addObject:gift];
NSLog(@"加入队列,当前队列长度: %lu", (unsigned long)self.giftQueue.count);
}
}
//
[self processGiftQueue];
}
- (QXGiftDisplayView *)findDisplayingViewForGift:(QXRoomChatListModel *)gift {
for (QXGiftDisplayView *view in self.displayViews) {
if (view.isAnimating && [view.currentGift isSameGiftFromSameSender:gift]) {
return view;
}
}
return nil;
}
- (QXGiftDisplayView *)findAvailableDisplayView {
for (QXGiftDisplayView *view in self.displayViews) {
if (!view.isAnimating) {
return view;
}
}
return nil;
}
- (void)processGiftQueue {
if (self.isProcessingQueue) {
return;
}
self.isProcessingQueue = YES;
//
while (self.giftQueue.count > 0) {
QXGiftDisplayView *availableView = [self findAvailableDisplayView];
if (!availableView) {
break;
}
QXRoomChatListModel *gift = self.giftQueue.firstObject;
[self.giftQueue removeObjectAtIndex:0];
//
QXGiftDisplayView *displayingView = [self findDisplayingViewForGift:gift];
if (!displayingView) {
NSString *key = [self giftKeyForGift:gift];
self.accumulatedGifts[key] = [gift copy];
[availableView showGift:gift];
NSLog(@"从队列显示礼物: %@", gift.GiftInfo.gift_name);
} else {
//
NSString *key = [self giftKeyForGift:gift];
QXRoomChatListModel *accumulatedGift = self.accumulatedGifts[key];
if (accumulatedGift) {
NSInteger gift_num = accumulatedGift.gift_num.integerValue;
gift_num += gift.gift_num.integerValue;
accumulatedGift.gift_num = [NSString stringWithFormat:@"%ld",gift_num];
[displayingView updateGiftCount:accumulatedGift.gift_num.integerValue];
NSLog(@"队列礼物累加到现有显示: %@ x%@", gift.GiftInfo.gift_name, accumulatedGift.gift_num);
}
}
}
self.isProcessingQueue = NO;
//
if (self.giftQueue.count > 0) {
NSLog(@"队列中还有 %lu 个礼物等待显示", (unsigned long)self.giftQueue.count);
}
}
- (NSString *)giftKeyForGift:(QXRoomChatListModel *)gift {
return [NSString stringWithFormat:@"%@_%@_%@", gift.FromUserInfo.nickname ?: @"unknown", gift.GiftInfo.gift_id ?: @"unknown",gift.ToUserInfo.nickname ?: @"unknown"];
}
#pragma mark - QXGiftDisplayViewDelegate
- (void)QXGiftDisplayViewDidFinishAnimation:(QXGiftDisplayView *)view {
NSLog(@"飘屏动画结束: %ld", (long)view.tag);
//
if (view.currentGift) {
NSString *key = [self giftKeyForGift:view.currentGift];
[self.accumulatedGifts removeObjectForKey:key];
NSLog(@"移除累加记录: %@", key);
}
//
view.currentGift = nil;
view.isAnimating = NO;
//
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self processGiftQueue];
});
}
- (void)clearAll {
NSLog(@"清空所有礼物和队列");
for (QXGiftDisplayView *view in self.displayViews) {
[view finishAnimationImmediately];
[view removeFromSuperview];
}
[self.displayViews removeAllObjects];
[self.giftQueue removeAllObjects];
[self.accumulatedGifts removeAllObjects];
[self.waitingUpdateGifts removeAllObjects];
self.isProcessingQueue = NO;
}
//
- (void)printDebugInfo {
NSLog(@"=== 飘屏管理器状态 ===");
NSLog(@"队列长度: %lu", (unsigned long)self.giftQueue.count);
NSLog(@"累加记录: %lu", (unsigned long)self.accumulatedGifts.count);
for (int i = 0; i < self.displayViews.count; i++) {
QXGiftDisplayView *view = self.displayViews[i];
NSLog(@"视图 %d: 动画中=%@, 礼物=%@", i, view.isAnimating ? @"是" : @"否", view.currentGift.GiftInfo.gift_name ?: @"无");
}
NSLog(@"====================");
}
@end

View File

@@ -0,0 +1,27 @@
//
// QXQXGiftDisplayView.h
// QXLive
//
// Created by 启星 on 2025/10/20.
//
#import <UIKit/UIKit.h>
@class QXGiftDisplayView;
@protocol QXGiftDisplayViewDelegate <NSObject>
- (void)QXGiftDisplayViewDidFinishAnimation:(QXGiftDisplayView *)view;
@end
@interface QXGiftDisplayView : UIView
@property (nonatomic, weak) id<QXGiftDisplayViewDelegate> delegate;
@property (nonatomic, assign) BOOL isAnimating; // 是否正在动画
@property (nonatomic, strong) QXRoomChatListModel *currentGift; // 当前显示的礼物
// 显示礼物
- (void)showGift:(QXRoomChatListModel *)gift;
// 更新礼物数量
- (void)updateGiftCount:(NSInteger)count;
// 立即结束动画(用于队列管理)
- (void)finishAnimationImmediately;
@end

View File

@@ -0,0 +1,217 @@
//
// QXGiftDisplayView.m
// Test
//
// Created by on 2025/10/20.
//
// QXGiftDisplayView.m
#import "QXGiftDisplayView.h"
@interface QXGiftDisplayView ()
@property (nonatomic, strong) UIImageView *avatarImageView;
@property (nonatomic, strong) UILabel *senderLabel;
@property (nonatomic, strong) UILabel *giftLabel;
@property (nonatomic, strong) UILabel *countLabel;
@property (nonatomic, strong) UIImageView *giftImageView;
@property (nonatomic, strong) NSTimer *hideTimer;
@end
@implementation QXGiftDisplayView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupUI];
self.isAnimating = NO;
}
return self;
}
- (void)setupUI {
self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.7];
self.layer.cornerRadius = 20;
self.layer.masksToBounds = YES;
//
self.avatarImageView = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 30, 30)];
self.avatarImageView.layer.cornerRadius = 15;
self.avatarImageView.layer.masksToBounds = YES;
self.avatarImageView.backgroundColor = [UIColor lightGrayColor];
self.avatarImageView.contentMode = UIViewContentModeScaleAspectFill;
[self addSubview:self.avatarImageView];
//
self.senderLabel = [[UILabel alloc] initWithFrame:CGRectMake(40, 5, 120, 15)];
self.senderLabel.font = [UIFont boldSystemFontOfSize:12];
self.senderLabel.textColor = [UIColor whiteColor];
[self addSubview:self.senderLabel];
//
self.giftLabel = [[UILabel alloc] initWithFrame:CGRectMake(40, 20, 120, 15)];
self.giftLabel.font = [UIFont systemFontOfSize:11];
self.giftLabel.textColor = [UIColor yellowColor];
[self addSubview:self.giftLabel];
//
self.giftImageView = [[UIImageView alloc] initWithFrame:CGRectMake(165, 5, 30, 30)];
// self.giftImageView.backgroundColor = [UIColor orangeColor];
self.giftImageView.contentMode = UIViewContentModeScaleAspectFit;
self.giftImageView.layer.cornerRadius = 5;
self.giftImageView.layer.masksToBounds = YES;
[self addSubview:self.giftImageView];
//
self.countLabel = [[UILabel alloc] initWithFrame:CGRectMake(200, 5, 60, 30)];
self.countLabel.font = [UIFont boldSystemFontOfSize:16];
self.countLabel.textColor = [UIColor redColor];
self.countLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:self.countLabel];
//
self.frame = CGRectMake(-self.bounds.size.width, self.frame.origin.y, self.bounds.size.width, self.bounds.size.height);
}
// updateUIWithGift
- (void)updateUIWithGift:(QXRoomChatListModel *)gift {
if (!gift) return;
//
self.senderLabel.text = [NSString stringWithFormat:@"%@ 送给 %@",gift.FromUserInfo.nickname?: @"",gift.ToUserInfo.nickname?: @""];
//
self.giftLabel.text = [NSString stringWithFormat:@"%@", gift.GiftInfo.gift_name ?: @"礼物"];
//
self.countLabel.text = [NSString stringWithFormat:@"x%@", gift.gift_num];
// 使SDWebImage
[self.avatarImageView sd_setImageWithURL:[NSURL URLWithString:gift.FromUserInfo.avatar]];
//
[self.giftImageView sd_setImageWithURL:[NSURL URLWithString:gift.GiftInfo.base_image]];
}
//
- (UIColor *)randomColor {
CGFloat red = arc4random_uniform(255) / 255.0;
CGFloat green = arc4random_uniform(255) / 255.0;
CGFloat blue = arc4random_uniform(255) / 255.0;
return [UIColor colorWithRed:red green:green blue:blue alpha:1.0];
}
- (void)showGift:(QXRoomChatListModel *)gift {
//
if (self.isAnimating) {
NSLog(@"警告: 视图正在动画中,无法显示新礼物");
return;
}
self.currentGift = gift;
self.isAnimating = YES;
// UI -
[self updateUIWithGift:gift];
//
self.frame = CGRectMake(-self.bounds.size.width, self.frame.origin.y, self.bounds.size.width, self.bounds.size.height);
self.alpha = 1.0;
//
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
self.frame = CGRectMake(10, self.frame.origin.y, self.bounds.size.width, self.bounds.size.height);
} completion:^(BOOL finished) {
if (finished) {
[self startHideTimer];
} else {
//
self.isAnimating = NO;
self.currentGift = nil;
}
}];
}
- (void)updateGiftCount:(NSInteger)count {
if (!self.isAnimating) {
NSLog(@"警告: 视图不在动画中,无法更新数量");
return;
}
NSLog(@"更新礼物数量: %ld", (long)count);
//
self.countLabel.text = [NSString stringWithFormat:@"x%ld", (long)count];
//
[UIView animateWithDuration:0.2 animations:^{
self.countLabel.transform = CGAffineTransformMakeScale(1.5, 1.5);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.2 animations:^{
self.countLabel.transform = CGAffineTransformIdentity;
}];
//
[self resetHideTimer];
}];
}
- (void)startHideTimer {
[self.hideTimer invalidate];
self.hideTimer = [NSTimer scheduledTimerWithTimeInterval:3.0
target:self
selector:@selector(hideAnimation)
userInfo:nil
repeats:NO];
}
- (void)resetHideTimer {
[self.hideTimer invalidate];
[self startHideTimer];
}
- (void)hideAnimation {
if (!self.isAnimating) {
return;
}
[UIView animateWithDuration:0.5
animations:^{
self.frame = CGRectMake(-self.bounds.size.width, self.frame.origin.y, self.bounds.size.width, self.bounds.size.height);
self.alpha = 0.0;
} completion:^(BOOL finished) {
self.isAnimating = NO;
if ([self.delegate respondsToSelector:@selector(QXGiftDisplayViewDidFinishAnimation:)]) {
[self.delegate QXGiftDisplayViewDidFinishAnimation:self];
}
self.currentGift = nil;
}];
}
- (void)finishAnimationImmediately {
NSLog(@"立即结束动画");
[self.hideTimer invalidate];
self.hideTimer = nil;
[self.layer removeAllAnimations];
self.isAnimating = NO;
self.currentGift = nil;
self.alpha = 0.0;
self.frame = CGRectMake(-self.bounds.size.width, self.frame.origin.y, self.bounds.size.width, self.bounds.size.height);
}
- (void)dealloc {
[self.hideTimer invalidate];
self.hideTimer = nil;
NSLog(@"QXGiftDisplayView dealloc");
}
@end

View File

@@ -376,7 +376,9 @@ static NSInteger maxSeat = 8;
NSString* tmpPitNum = [NSString stringWithFormat:@"%@",fromPitModel.pit_number];
fromPitModel.pit_number = totModel.pit_number;
totModel.pit_number = tmpPitNum;
NSMutableArray*arr = [NSMutableArray arrayWithArray:self.roomModel.room_info.pit_list];
[arr exchangeObjectAtIndex:fromPitNumber-1 withObjectAtIndex:toPitNumber-1];
self.roomModel.room_info.pit_list = arr;
toSeatView.pitModel = fromPitModel;
fromSeatView.pitModel = totModel;
if ([userInfo.user_id isEqualToString:QXGlobal.shareGlobal.loginModel.user_id]) {