initial commit
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// AVSettingsViewController.h
|
||||
// AVDemo
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AVSettingsViewController : UIViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,282 @@
|
||||
//
|
||||
// AVSettingsViewController.m
|
||||
// AVDemo
|
||||
//
|
||||
|
||||
#import "AVSettingsViewController.h"
|
||||
#import "AVConfigManager.h"
|
||||
#import "AVConstants.h"
|
||||
#import "AVUserManager.h"
|
||||
#import <Masonry/Masonry.h>
|
||||
#import "UIView+SellyCloud.h"
|
||||
|
||||
@interface AVSettingsViewController () <UITextFieldDelegate>
|
||||
@property (nonatomic, strong) UIScrollView *scrollView;
|
||||
@property (nonatomic, strong) UIView *contentView;
|
||||
|
||||
@property (nonatomic, strong) UITextField *nicknameField;
|
||||
@property (nonatomic, strong) UISegmentedControl *resolutionSegment;
|
||||
@property (nonatomic, strong) UITextField *fpsField;
|
||||
@property (nonatomic, strong) UITextField *maxBitrateField;
|
||||
@property (nonatomic, strong) UITextField *minBitrateField;
|
||||
@end
|
||||
|
||||
@implementation AVSettingsViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [UIColor systemBackgroundColor];
|
||||
self.title = @"设置";
|
||||
|
||||
// 添加右上角提交日志按钮 - 延迟设置以避免约束冲突
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIBarButtonItem *uploadLogButton = [[UIBarButtonItem alloc] initWithTitle:@"提交日志"
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(uploadLogButtonTapped)];
|
||||
self.navigationItem.rightBarButtonItem = uploadLogButton;
|
||||
});
|
||||
|
||||
[self setupUI];
|
||||
[self loadConfig];
|
||||
}
|
||||
|
||||
- (void)setupUI {
|
||||
_scrollView = [[UIScrollView alloc] init];
|
||||
[self.view addSubview:_scrollView];
|
||||
|
||||
_contentView = [[UIView alloc] init];
|
||||
[_scrollView addSubview:_contentView];
|
||||
|
||||
[_scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
|
||||
make.left.right.bottom.equalTo(self.view);
|
||||
}];
|
||||
|
||||
[_contentView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.edges.equalTo(_scrollView);
|
||||
make.width.equalTo(_scrollView);
|
||||
}];
|
||||
|
||||
CGFloat topOffset = 20;
|
||||
CGFloat spacing = 30;
|
||||
|
||||
// Nickname
|
||||
topOffset = [self addLabel:@"用户昵称" atOffset:topOffset];
|
||||
_nicknameField = [self addTextField:@"请输入昵称" atOffset:topOffset];
|
||||
topOffset += 40 + spacing;
|
||||
|
||||
// Resolution
|
||||
topOffset = [self addLabel:@"分辨率" atOffset:topOffset];
|
||||
_resolutionSegment = [[UISegmentedControl alloc] initWithItems:@[@"360p", @"480p", @"540p", @"720p"]];
|
||||
[_contentView addSubview:_resolutionSegment];
|
||||
|
||||
[_resolutionSegment mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(_contentView).offset(20);
|
||||
make.right.equalTo(_contentView).offset(-20);
|
||||
make.top.equalTo(_contentView).offset(topOffset);
|
||||
make.height.equalTo(@32);
|
||||
}];
|
||||
topOffset += 32 + spacing;
|
||||
|
||||
// FPS
|
||||
topOffset = [self addLabel:@"帧率 (FPS)" atOffset:topOffset];
|
||||
_fpsField = [self addTextField:@"例如: 30" atOffset:topOffset];
|
||||
_fpsField.keyboardType = UIKeyboardTypeNumberPad;
|
||||
topOffset += 40 + spacing;
|
||||
|
||||
// Max bitrate
|
||||
topOffset = [self addLabel:@"最大码率 (kbps)" atOffset:topOffset];
|
||||
_maxBitrateField = [self addTextField:@"例如: 2000" atOffset:topOffset];
|
||||
_maxBitrateField.keyboardType = UIKeyboardTypeNumberPad;
|
||||
topOffset += 40 + spacing;
|
||||
|
||||
// Min bitrate
|
||||
topOffset = [self addLabel:@"最小码率 (kbps)" atOffset:topOffset];
|
||||
_minBitrateField = [self addTextField:@"例如: 500" atOffset:topOffset];
|
||||
_minBitrateField.keyboardType = UIKeyboardTypeNumberPad;
|
||||
topOffset += 40 + spacing;
|
||||
|
||||
// Save button
|
||||
UIButton *saveButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[saveButton setTitle:@"保存设置" forState:UIControlStateNormal];
|
||||
saveButton.backgroundColor = [UIColor systemBlueColor];
|
||||
[saveButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
saveButton.titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightSemibold];
|
||||
saveButton.layer.cornerRadius = 10;
|
||||
[saveButton addTarget:self action:@selector(saveConfig) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_contentView addSubview:saveButton];
|
||||
|
||||
[saveButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(_contentView).offset(20);
|
||||
make.right.equalTo(_contentView).offset(-20);
|
||||
make.top.equalTo(_contentView).offset(topOffset);
|
||||
make.height.equalTo(@50);
|
||||
}];
|
||||
topOffset += 50 + 20;
|
||||
|
||||
// Logout button
|
||||
UIButton *logoutButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[logoutButton setTitle:@"退出登录" forState:UIControlStateNormal];
|
||||
logoutButton.backgroundColor = [UIColor systemRedColor];
|
||||
[logoutButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
logoutButton.titleLabel.font = [UIFont systemFontOfSize:17 weight:UIFontWeightSemibold];
|
||||
logoutButton.layer.cornerRadius = 10;
|
||||
[logoutButton addTarget:self action:@selector(logoutButtonTapped) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_contentView addSubview:logoutButton];
|
||||
|
||||
[logoutButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(_contentView).offset(20);
|
||||
make.right.equalTo(_contentView).offset(-20);
|
||||
make.top.equalTo(_contentView).offset(topOffset);
|
||||
make.height.equalTo(@50);
|
||||
}];
|
||||
topOffset += 50 + 20;
|
||||
|
||||
// 设置 contentView 的高度
|
||||
[_contentView mas_updateConstraints:^(MASConstraintMaker *make) {
|
||||
make.height.equalTo(@(topOffset));
|
||||
}];
|
||||
|
||||
// Dismiss keyboard on tap
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)];
|
||||
[self.view addGestureRecognizer:tap];
|
||||
}
|
||||
|
||||
- (CGFloat)addLabel:(NSString *)text atOffset:(CGFloat)offset {
|
||||
UILabel *label = [[UILabel alloc] init];
|
||||
label.text = text;
|
||||
label.font = [UIFont systemFontOfSize:15 weight:UIFontWeightMedium];
|
||||
label.textColor = [UIColor labelColor];
|
||||
[_contentView addSubview:label];
|
||||
|
||||
[label mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(_contentView).offset(20);
|
||||
make.top.equalTo(_contentView).offset(offset);
|
||||
}];
|
||||
|
||||
return offset + 25;
|
||||
}
|
||||
|
||||
- (UITextField *)addTextField:(NSString *)placeholder atOffset:(CGFloat)offset {
|
||||
UITextField *textField = [[UITextField alloc] init];
|
||||
textField.placeholder = placeholder;
|
||||
textField.borderStyle = UITextBorderStyleRoundedRect;
|
||||
textField.delegate = self;
|
||||
[_contentView addSubview:textField];
|
||||
|
||||
[textField mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||
make.left.equalTo(_contentView).offset(20);
|
||||
make.right.equalTo(_contentView).offset(-20);
|
||||
make.top.equalTo(_contentView).offset(offset);
|
||||
make.height.equalTo(@40);
|
||||
}];
|
||||
|
||||
return textField;
|
||||
}
|
||||
|
||||
- (void)loadConfig {
|
||||
AVVideoConfiguration *config = [AVConfigManager sharedManager].globalConfig;
|
||||
|
||||
_nicknameField.text = config.nickname;
|
||||
_resolutionSegment.selectedSegmentIndex = [config currentResolution];
|
||||
_fpsField.text = [NSString stringWithFormat:@"%ld", (long)config.videoFrameRate];
|
||||
_maxBitrateField.text = [NSString stringWithFormat:@"%ld", (long)(config.videoBitRate / 1000)];
|
||||
_minBitrateField.text = [NSString stringWithFormat:@"%ld", (long)(config.videoMinBitRate / 1000)];
|
||||
}
|
||||
|
||||
- (void)saveConfig {
|
||||
AVVideoConfiguration *config = [AVConfigManager sharedManager].globalConfig;
|
||||
|
||||
config.nickname = _nicknameField.text.length > 0 ? _nicknameField.text : [AVConfigManager generateRandomNickname];
|
||||
[config setResolution:_resolutionSegment.selectedSegmentIndex];
|
||||
|
||||
NSInteger fps = [_fpsField.text integerValue] > 0 ? [_fpsField.text integerValue] : 30;
|
||||
config.videoFrameRate = fps;
|
||||
config.videoMaxKeyframeInterval = fps * 2;
|
||||
|
||||
NSInteger maxBitrate = [_maxBitrateField.text integerValue] > 0 ? [_maxBitrateField.text integerValue] : 2000;
|
||||
config.videoBitRate = maxBitrate * 1000; // Convert kbps to bps
|
||||
|
||||
NSInteger minBitrate = [_minBitrateField.text integerValue] > 0 ? [_minBitrateField.text integerValue] : 500;
|
||||
config.videoMinBitRate = minBitrate * 1000; // Convert kbps to bps
|
||||
|
||||
[[AVConfigManager sharedManager] saveConfig];
|
||||
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"成功" message:@"设置已保存" preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)logoutButtonTapped {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"确认退出"
|
||||
message:@"确定要退出登录吗?"
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消"
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:nil];
|
||||
[alert addAction:cancelAction];
|
||||
|
||||
UIAlertAction *logoutAction = [UIAlertAction actionWithTitle:@"退出"
|
||||
style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction * _Nonnull action) {
|
||||
// 执行登出
|
||||
[[AVUserManager sharedManager] logout];
|
||||
|
||||
// 返回到登录页面
|
||||
// 通过 TabBarController 触发登录检查
|
||||
if ([self.tabBarController respondsToSelector:@selector(checkLoginStatus)]) {
|
||||
[self.tabBarController performSelector:@selector(checkLoginStatus)];
|
||||
}
|
||||
|
||||
NSLog(@"✅ 已退出登录");
|
||||
}];
|
||||
[alert addAction:logoutAction];
|
||||
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)uploadLogButtonTapped {
|
||||
// 调用 SellyCloudManager 的 uploadLog 方法
|
||||
[SellyCloudManager uploadLog:^(NSString * _Nullable url, NSError * _Nullable error) {
|
||||
UIAlertController *resultAlert;
|
||||
if (error) {
|
||||
// 上传失败
|
||||
resultAlert = [UIAlertController alertControllerWithTitle:@"上传失败"
|
||||
message:[NSString stringWithFormat:@"日志上传失败: %@", error.localizedDescription]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[resultAlert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
|
||||
} else {
|
||||
// 上传成功
|
||||
NSString *message = url ? [NSString stringWithFormat:@"日志已成功上传\n地址: %@", url] : @"日志已成功上传";
|
||||
resultAlert = [UIAlertController alertControllerWithTitle:@"上传成功"
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
NSLog(@"###日志url == %@",url);
|
||||
|
||||
// 点击确定后将 URL 复制到粘贴板
|
||||
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确定"
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * _Nonnull action) {
|
||||
if (url.length > 0) {
|
||||
UIPasteboard.generalPasteboard.string = url;
|
||||
[self.view showToast:@"日志 URL 已复制到粘贴板"];
|
||||
}
|
||||
}];
|
||||
[resultAlert addAction:confirmAction];
|
||||
}
|
||||
[self presentViewController:resultAlert animated:YES completion:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)dismissKeyboard {
|
||||
[self.view endEditing:YES];
|
||||
}
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||||
[textField resignFirstResponder];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user