initial commit
This commit is contained in:
260
Example/SellyCloudSDK/Play/SCPlayerDebugView.m
Normal file
260
Example/SellyCloudSDK/Play/SCPlayerDebugView.m
Normal file
@@ -0,0 +1,260 @@
|
||||
//
|
||||
// SCPlayerDebugView.m
|
||||
// SellyCloudSDK_Example
|
||||
//
|
||||
// Created by Caleb on 07/1/26.
|
||||
// Copyright © 2026 Caleb. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SCPlayerDebugView.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
|
||||
@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<NSString *> *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
|
||||
Reference in New Issue
Block a user