Files
peko-ios/YuMi/Modules/YMRoom/View/AnimationView/BannerScheduler.m

231 lines
6.4 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// BannerScheduler.m
// YuMi
//
// Created by AI Assistant on 2025/1/13.
//
#import "BannerScheduler.h"
@interface BannerScheduler ()
@property (nonatomic, strong) NSMutableArray *bannerQueue;
@property (nonatomic, assign) BOOL isPlaying;
@property (nonatomic, assign) BOOL isPaused;
@end
@implementation BannerScheduler
#pragma mark - Initialization
- (instancetype)initWithDelegate:(id<BannerSchedulerDelegate>)delegate {
if (self = [super init]) {
_delegate = delegate;
_bannerQueue = [NSMutableArray array];
_isPlaying = NO;
_isPaused = NO;
}
return self;
}
#pragma mark - Public Methods
- (void)enqueueBanner:(id)banner {
if (!banner) {
return;
}
[self.bannerQueue addObject:banner];
// 如果当前没有在播放且未暂停,则开始处理
if (!self.isPlaying && !self.isPaused) {
[self processNextBanner];
}
}
- (void)processNextBanner {
if (self.isPaused) {
return;
}
// 🔧 新增:检查 delegate 是否有效
if (!self.delegate) {
[self clearQueue];
self.isPlaying = NO;
return;
}
if (self.isPaused) {
return;
}
if (self.bannerQueue.count == 0) {
self.isPlaying = NO;
return;
}
if (self.isPlaying) {
return;
}
// 对队列进行优先级排序
[self sortQueueByPriority];
// 取出队列中的第一个 Banner
id nextBanner = [self.bannerQueue firstObject];
[self.bannerQueue removeObjectAtIndex:0];
self.isPlaying = YES;
// 通知代理开始播放
if ([self.delegate respondsToSelector:@selector(bannerScheduler:didStartPlayingBanner:)]) {
[self.delegate bannerScheduler:self didStartPlayingBanner:nextBanner];
}
// 通知代理播放 Banner
if ([self.delegate respondsToSelector:@selector(bannerScheduler:shouldPlayBanner:)]) {
[self.delegate bannerScheduler:self shouldPlayBanner:nextBanner];
}
}
- (void)clearQueue {
[self.bannerQueue removeAllObjects];
}
- (void)sortQueueByPriority {
// 保持先进先出FIFO策略不需要排序
// 队列顺序就是添加顺序,确保公平性
NSLog(@"🔄 BannerScheduler: 使用先进先出策略,保持队列原有顺序");
}
- (void)pause {
if (self.isPaused) {
NSLog(@"⏸️ BannerScheduler: 调度器已经处于暂停状态");
return;
}
NSLog(@"⏸️ BannerScheduler: 暂停调度器");
self.isPaused = YES;
}
- (void)resume {
NSLog(@"▶️ BannerScheduler: 恢复调度器");
self.isPaused = NO;
// 如果当前没有在播放,则开始处理队列
if (!self.isPlaying) {
[self processNextBanner];
}
}
- (BOOL)isQueueEmpty {
return self.bannerQueue.count == 0;
}
- (nullable id)bannerAtIndex:(NSInteger)index {
if (index < 0 || index >= self.bannerQueue.count) {
return nil;
}
return [self.bannerQueue objectAtIndex:index];
}
- (BOOL)removeBannerAtIndex:(NSInteger)index {
if (index < 0 || index >= self.bannerQueue.count) {
return NO;
}
id removedBanner = [self.bannerQueue objectAtIndex:index];
[self.bannerQueue removeObjectAtIndex:index];
NSLog(@"🗑️ BannerScheduler: 从队列中移除 Banner - 索引: %ld, 类型: %@",
(long)index, [removedBanner class]);
return YES;
}
- (NSString *)queueStatusDescription {
NSMutableString *description = [NSMutableString string];
[description appendFormat:@"BannerScheduler 状态:\n"];
[description appendFormat:@"- 播放状态: %@\n", self.isPlaying ? @"播放中" : @"空闲"];
[description appendFormat:@"- 暂停状态: %@\n", self.isPaused ? @"已暂停" : @"运行中"];
[description appendFormat:@"- 队列长度: %ld\n", (long)self.bannerQueue.count];
if (self.bannerQueue.count > 0) {
[description appendString:@"- 队列内容:\n"];
for (NSInteger i = 0; i < self.bannerQueue.count; i++) {
id banner = self.bannerQueue[i];
[description appendFormat:@" [%ld] 类型: %@\n", (long)i, [banner class]];
}
}
return description;
}
#pragma mark - Public Properties
- (NSInteger)queueCount {
return self.bannerQueue.count;
}
#pragma mark - Internal Methods
/**
* 标记 Banner 播放完成
* 这个方法应该由代理在 Banner 播放完成后调用
*/
- (void)markBannerFinished {
if (!self.isPlaying) {
NSLog(@"⚠️ BannerScheduler: 尝试标记未播放的 Banner 为完成");
return;
}
// 防止过快标记完成,确保 banner 有足够的显示时间
static NSTimeInterval lastFinishTime = 0;
NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970];
// 如果距离上次完成时间太短,延迟处理
if (currentTime - lastFinishTime < 0.3) {
NSLog(@"⏳ BannerScheduler: Banner 完成间隔过短,延迟处理 (间隔: %.2f秒)", currentTime - lastFinishTime);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self markBannerFinished];
});
return;
}
lastFinishTime = currentTime;
NSLog(@"✅ BannerScheduler: Banner 播放完成");
self.isPlaying = NO;
// 通知代理播放完成
if ([self.delegate respondsToSelector:@selector(bannerSchedulerDidFinishPlaying:)]) {
[self.delegate bannerSchedulerDidFinishPlaying:self];
}
// 处理队列中的下一个 Banner
[self processNextBanner];
}
/**
* 调试方法:检查调度器状态
* @return 调试信息字符串
*/
- (NSString *)debugStatus {
NSMutableString *debugInfo = [NSMutableString string];
[debugInfo appendFormat:@"BannerScheduler Debug Status:\n"];
[debugInfo appendFormat:@"- 播放状态: %@\n", self.isPlaying ? @"播放中" : @"空闲"];
[debugInfo appendFormat:@"- 暂停状态: %@\n", self.isPaused ? @"已暂停" : @"运行中"];
[debugInfo appendFormat:@"- 队列长度: %ld\n", (long)self.bannerQueue.count];
if (self.bannerQueue.count > 0) {
[debugInfo appendString:@"- 队列内容:\n"];
for (NSInteger i = 0; i < self.bannerQueue.count; i++) {
id banner = self.bannerQueue[i];
[debugInfo appendFormat:@" [%ld] 类型: %@\n", (long)i, [banner class]];
}
}
return debugInfo;
}
@end