// // SCPlayerDebugView.m // SellyCloudSDK_Example // // Created by Caleb on 07/1/26. // Copyright © 2026 Caleb. All rights reserved. // #import "SCPlayerDebugView.h" #import @interface SCPlayerDebugView () @property (nonatomic, strong) UIVisualEffectView *blurView; @property (nonatomic, strong) UIView *headerView; @property (nonatomic, strong) UIView *contentView; @property (nonatomic, strong) UIScrollView *scrollView; @property (nonatomic, strong) UILabel *titleLabel; @property (nonatomic, strong) UILabel *logLabel; @property (nonatomic, strong) UIButton *toggleButton; @property (nonatomic, strong) UIButton *clearButton; @property (nonatomic, strong) MASConstraint *contentViewHeightConstraint; @property (nonatomic, strong) NSMutableArray *logs; @property (nonatomic, strong) NSDateFormatter *dateFormatter; @end @implementation SCPlayerDebugView - (instancetype)init { self = [super init]; if (self) { _isExpanded = YES; // 默认展开 _logs = [NSMutableArray array]; // 日期格式化器(用于时间戳) _dateFormatter = [[NSDateFormatter alloc] init]; _dateFormatter.dateFormat = @"HH:mm:ss.SSS"; [self setupView]; } return self; } - (void)setupView { // 毛玻璃背景 UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemUltraThinMaterialDark]; _blurView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; _blurView.layer.cornerRadius = 12; _blurView.layer.masksToBounds = YES; [self addSubview:_blurView]; [_blurView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(self); }]; // 边框 self.layer.cornerRadius = 12; self.layer.borderWidth = 0.5; self.layer.borderColor = [[UIColor whiteColor] colorWithAlphaComponent:0.3].CGColor; // 标题栏(可点击) _headerView = [[UIView alloc] init]; [_blurView.contentView addSubview:_headerView]; [_headerView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.left.right.equalTo(_blurView.contentView); make.height.offset(44); }]; // 添加点击手势 UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleButtonTapped)]; [_headerView addGestureRecognizer:tap]; // 标题 _titleLabel = [[UILabel alloc] init]; _titleLabel.text = @"🐛 调试日志"; _titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightSemibold]; _titleLabel.textColor = [UIColor whiteColor]; [_headerView addSubview:_titleLabel]; [_titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.equalTo(_headerView); make.left.offset(12); }]; // 清空按钮 _clearButton = [UIButton buttonWithType:UIButtonTypeSystem]; [_clearButton setImage:[UIImage systemImageNamed:@"trash.circle.fill"] forState:UIControlStateNormal]; _clearButton.tintColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8]; [_clearButton addTarget:self action:@selector(clearButtonTapped) forControlEvents:UIControlEventTouchUpInside]; [_headerView addSubview:_clearButton]; [_clearButton mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.equalTo(_headerView); make.right.offset(-44); make.width.height.offset(24); }]; // 展开/收起按钮 _toggleButton = [UIButton buttonWithType:UIButtonTypeSystem]; // 默认展开状态,显示向上箭头(表示可以收起) [_toggleButton setImage:[UIImage systemImageNamed:@"chevron.up.circle.fill"] forState:UIControlStateNormal]; _toggleButton.tintColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8]; [_toggleButton addTarget:self action:@selector(toggleButtonTapped) forControlEvents:UIControlEventTouchUpInside]; [_headerView addSubview:_toggleButton]; [_toggleButton mas_makeConstraints:^(MASConstraintMaker *make) { make.centerY.equalTo(_headerView); make.right.offset(-12); make.width.height.offset(24); }]; // 分割线 UIView *separatorLine = [[UIView alloc] init]; separatorLine.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.2]; [_blurView.contentView addSubview:separatorLine]; [separatorLine mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(_headerView.mas_bottom); make.left.offset(12); make.right.offset(-12); make.height.offset(0.5); }]; // 内容容器 _contentView = [[UIView alloc] init]; _contentView.clipsToBounds = YES; [_blurView.contentView addSubview:_contentView]; [_contentView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(separatorLine.mas_bottom); make.left.right.bottom.equalTo(_blurView.contentView); // 保存高度约束的引用,用于动态调整 self.contentViewHeightConstraint = make.height.offset(200); // 默认高度 }]; // ScrollView 用于滚动内容 _scrollView = [[UIScrollView alloc] init]; _scrollView.showsVerticalScrollIndicator = YES; _scrollView.showsHorizontalScrollIndicator = NO; _scrollView.alwaysBounceVertical = YES; [_contentView addSubview:_scrollView]; [_scrollView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(_contentView).insets(UIEdgeInsetsMake(8, 12, 8, 12)); }]; // 日志文本标签 _logLabel = [[UILabel alloc] init]; _logLabel.font = [UIFont monospacedSystemFontOfSize:10 weight:UIFontWeightRegular]; _logLabel.textColor = [UIColor whiteColor]; _logLabel.numberOfLines = 0; // 多行显示 _logLabel.lineBreakMode = NSLineBreakByCharWrapping; _logLabel.text = @""; // 初始为空 [_scrollView addSubview:_logLabel]; [_logLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(_scrollView); make.width.equalTo(_scrollView); // 限制宽度,允许文本换行 }]; } - (void)toggleButtonTapped { // 切换状态 [self setIsExpanded:!self.isExpanded]; } - (void)clearButtonTapped { [self clearLogs]; // 触觉反馈 UIImpactFeedbackGenerator *feedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleMedium]; [feedback impactOccurred]; } #pragma mark - Public Methods - (void)appendLog:(NSString *)message { [self appendLog:message withPrefix:nil]; } - (void)appendLog:(NSString *)message withPrefix:(nullable NSString *)prefix { if (!message || message.length == 0) { return; } // 生成时间戳 NSString *timestamp = [self.dateFormatter stringFromDate:[NSDate date]]; // 构建完整的日志条目 NSString *logEntry; // if (prefix && prefix.length > 0) { // logEntry = [NSString stringWithFormat:@"[%@] %@ %@", timestamp, prefix, message]; // } else { logEntry = [NSString stringWithFormat:@"[%@] %@", timestamp, message]; // } // 添加到日志数组 [self.logs addObject:logEntry]; // 限制日志数量(最多保留 200 条) if (self.logs.count > 200) { [self.logs removeObjectAtIndex:0]; } // 更新 UI [self updateLogDisplay]; } - (void)clearLogs { [self.logs removeAllObjects]; [self updateLogDisplay]; } #pragma mark - Private Methods - (void)updateLogDisplay { // 将所有日志拼接成一个字符串 NSString *allLogs = [self.logs componentsJoinedByString:@"\n"]; self.logLabel.text = allLogs; // 更新布局 [self.logLabel mas_updateConstraints:^(MASConstraintMaker *make) { // 强制刷新高度 }]; [self.logLabel layoutIfNeeded]; // 滚动到底部(显示最新的日志) dispatch_async(dispatch_get_main_queue(), ^{ CGFloat bottomOffset = MAX(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height); [self.scrollView setContentOffset:CGPointMake(0, bottomOffset) animated:YES]; }); } - (void)setIsExpanded:(BOOL)isExpanded { if (_isExpanded == isExpanded) { return; // 状态相同,不执行任何操作 } _isExpanded = isExpanded; // 执行动画更新 UI CGFloat expandedHeight = 200; // 展开时的高度 CGFloat collapsedHeight = 0; // 收起时的高度 [UIView animateWithDuration:0.3 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0.5 options:UIViewAnimationOptionCurveEaseInOut animations:^{ if (self.isExpanded) { // 展开状态:显示向上箭头(表示可以收起) [self.toggleButton setImage:[UIImage systemImageNamed:@"chevron.up.circle.fill"] forState:UIControlStateNormal]; [self.contentViewHeightConstraint setOffset:expandedHeight]; } else { // 收起状态:显示向下箭头(表示可以展开) [self.toggleButton setImage:[UIImage systemImageNamed:@"chevron.down.circle.fill"] forState:UIControlStateNormal]; [self.contentViewHeightConstraint setOffset:collapsedHeight]; } // 显示/隐藏内容(用 alpha 做淡入淡出效果) self.contentView.alpha = self.isExpanded ? 1.0 : 0.0; // 更新约束触发布局更新 [self.superview layoutIfNeeded]; } completion:nil]; // 触觉反馈 UIImpactFeedbackGenerator *feedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight]; [feedback impactOccurred]; } @end