549 lines
25 KiB
Objective-C
549 lines
25 KiB
Objective-C
//
|
||
// AVSettingsView.m
|
||
// AVDemo
|
||
//
|
||
|
||
#import "AVSettingsView.h"
|
||
#import "AVConstants.h"
|
||
|
||
@interface AVSettingsView () <UITextFieldDelegate>
|
||
@property (nonatomic, copy) AVSettingsViewCallback callback;
|
||
@property (nonatomic, strong) AVVideoConfiguration *tempConfig;
|
||
@property (nonatomic, assign) AVSettingsFieldMask fieldsMask;
|
||
|
||
@property (nonatomic, strong) UIView *containerView;
|
||
@property (nonatomic, strong) UIScrollView *scrollView;
|
||
@property (nonatomic, strong) UIView *contentView;
|
||
@property (nonatomic, strong) UITextField *streamIdField;
|
||
@property (nonatomic, strong) UITextField *nicknameField;
|
||
@property (nonatomic, strong) UISegmentedControl *codecSegment;
|
||
@property (nonatomic, strong) UISegmentedControl *resolutionSegment;
|
||
@property (nonatomic, strong) UITextField *fpsField;
|
||
@property (nonatomic, strong) UITextField *maxBitrateField;
|
||
@property (nonatomic, strong) UITextField *minBitrateField;
|
||
|
||
@property (nonatomic, strong) NSLayoutConstraint *containerCenterYConstraint;
|
||
@property (nonatomic, weak) UITextField *activeTextField; // 当前激活的输入框
|
||
@end
|
||
|
||
@implementation AVSettingsView
|
||
|
||
- (instancetype)initWithFrame:(CGRect)frame {
|
||
if (self = [super initWithFrame:frame]) {
|
||
// UI will be setup when showing
|
||
|
||
// 监听键盘通知
|
||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||
selector:@selector(keyboardWillShow:)
|
||
name:UIKeyboardWillShowNotification
|
||
object:nil];
|
||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||
selector:@selector(keyboardWillHide:)
|
||
name:UIKeyboardWillHideNotification
|
||
object:nil];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (void)dealloc {
|
||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||
}
|
||
|
||
- (void)setupUI {
|
||
self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
|
||
|
||
// 在方法开始时统一计算屏幕尺寸和方向,避免重复定义
|
||
CGFloat screenHeight = CGRectGetHeight([UIScreen mainScreen].bounds);
|
||
CGFloat screenWidth = CGRectGetWidth([UIScreen mainScreen].bounds);
|
||
BOOL isLandscape = screenWidth > screenHeight;
|
||
|
||
_containerView = [[UIView alloc] init];
|
||
_containerView.backgroundColor = [UIColor systemBackgroundColor];
|
||
_containerView.layer.cornerRadius = 12;
|
||
_containerView.layer.masksToBounds = YES;
|
||
[self addSubview:_containerView];
|
||
|
||
_containerView.translatesAutoresizingMaskIntoConstraints = NO;
|
||
_containerCenterYConstraint = [_containerView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor];
|
||
|
||
// 根据屏幕方向动态调整容器高度
|
||
CGFloat maxHeight = isLandscape ? (screenHeight * 0.85) : 600; // 横屏时使用屏幕高度的85%
|
||
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[_containerView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:20],
|
||
[_containerView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-20],
|
||
_containerCenterYConstraint,
|
||
[_containerView.heightAnchor constraintLessThanOrEqualToConstant:maxHeight]
|
||
]];
|
||
|
||
// Add dismiss button
|
||
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||
[dismissButton setTitle:@"×" forState:UIControlStateNormal];
|
||
dismissButton.titleLabel.font = [UIFont systemFontOfSize:30];
|
||
[dismissButton addTarget:self action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside];
|
||
[_containerView addSubview:dismissButton];
|
||
|
||
dismissButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[dismissButton.topAnchor constraintEqualToAnchor:_containerView.topAnchor constant:10],
|
||
[dismissButton.trailingAnchor constraintEqualToAnchor:_containerView.trailingAnchor constant:-10],
|
||
[dismissButton.widthAnchor constraintEqualToConstant:44],
|
||
[dismissButton.heightAnchor constraintEqualToConstant:44]
|
||
]];
|
||
|
||
// Title
|
||
UILabel *titleLabel = [[UILabel alloc] init];
|
||
titleLabel.text = @"设置";
|
||
titleLabel.font = [UIFont systemFontOfSize:20 weight:UIFontWeightBold];
|
||
[_containerView addSubview:titleLabel];
|
||
|
||
titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[titleLabel.topAnchor constraintEqualToAnchor:_containerView.topAnchor constant:20],
|
||
[titleLabel.centerXAnchor constraintEqualToAnchor:_containerView.centerXAnchor]
|
||
]];
|
||
|
||
UIScrollView *scrollView = [[UIScrollView alloc] init];
|
||
scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive; // 允许滑动关闭键盘
|
||
[_containerView addSubview:scrollView];
|
||
_scrollView = scrollView;
|
||
|
||
// 动态调整 scrollView 和按钮之间的间距
|
||
CGFloat buttonAreaHeight = isLandscape ? 64 : 80; // 横屏时减小按钮区域高度
|
||
|
||
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[scrollView.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:20],
|
||
[scrollView.leadingAnchor constraintEqualToAnchor:_containerView.leadingAnchor],
|
||
[scrollView.trailingAnchor constraintEqualToAnchor:_containerView.trailingAnchor],
|
||
[scrollView.bottomAnchor constraintEqualToAnchor:_containerView.bottomAnchor constant:-buttonAreaHeight]
|
||
]];
|
||
|
||
UIView *contentView = [[UIView alloc] init];
|
||
[scrollView addSubview:contentView];
|
||
_contentView = contentView; // 保存引用
|
||
|
||
contentView.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[contentView.topAnchor constraintEqualToAnchor:scrollView.topAnchor],
|
||
[contentView.leadingAnchor constraintEqualToAnchor:scrollView.leadingAnchor],
|
||
[contentView.trailingAnchor constraintEqualToAnchor:scrollView.trailingAnchor],
|
||
[contentView.bottomAnchor constraintEqualToAnchor:scrollView.bottomAnchor],
|
||
[contentView.widthAnchor constraintEqualToAnchor:scrollView.widthAnchor]
|
||
]];
|
||
|
||
// Add form elements (similar to settings VC)
|
||
CGFloat topOffset = 10;
|
||
CGFloat spacing = 20;
|
||
|
||
// Stream ID (conditional)
|
||
if (self.fieldsMask & AVSettingsFieldStreamId) {
|
||
UILabel *streamIdLabel = [[UILabel alloc] init];
|
||
streamIdLabel.text = @"Stream ID";
|
||
streamIdLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||
[contentView addSubview:streamIdLabel];
|
||
streamIdLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[streamIdLabel.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[streamIdLabel.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset]
|
||
]];
|
||
topOffset += 20;
|
||
|
||
_streamIdField = [[UITextField alloc] init];
|
||
_streamIdField.placeholder = @"请输入Stream ID";
|
||
_streamIdField.borderStyle = UITextBorderStyleRoundedRect;
|
||
_streamIdField.delegate = self;
|
||
_streamIdField.returnKeyType = UIReturnKeyDone;
|
||
[contentView addSubview:_streamIdField];
|
||
_streamIdField.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[_streamIdField.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[_streamIdField.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor constant:-20],
|
||
[_streamIdField.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset],
|
||
[_streamIdField.heightAnchor constraintEqualToConstant:40]
|
||
]];
|
||
topOffset += 40 + spacing;
|
||
}
|
||
|
||
// Nickname (conditional)
|
||
if (self.fieldsMask & AVSettingsFieldNickname) {
|
||
UILabel *nicknameLabel = [[UILabel alloc] init];
|
||
nicknameLabel.text = @"用户昵称";
|
||
nicknameLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||
[contentView addSubview:nicknameLabel];
|
||
nicknameLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[nicknameLabel.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[nicknameLabel.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset]
|
||
]];
|
||
topOffset += 20;
|
||
|
||
_nicknameField = [[UITextField alloc] init];
|
||
_nicknameField.placeholder = @"请输入昵称";
|
||
_nicknameField.borderStyle = UITextBorderStyleRoundedRect;
|
||
_nicknameField.delegate = self;
|
||
_nicknameField.returnKeyType = UIReturnKeyDone;
|
||
[contentView addSubview:_nicknameField];
|
||
_nicknameField.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[_nicknameField.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[_nicknameField.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor constant:-20],
|
||
[_nicknameField.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset],
|
||
[_nicknameField.heightAnchor constraintEqualToConstant:40]
|
||
]];
|
||
topOffset += 40 + spacing;
|
||
}
|
||
|
||
// Video parameters (conditional)
|
||
if (self.fieldsMask & AVSettingsFieldVideoParams) {
|
||
// Resolution
|
||
UILabel *resolutionLabel = [[UILabel alloc] init];
|
||
resolutionLabel.text = @"分辨率";
|
||
resolutionLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||
[contentView addSubview:resolutionLabel];
|
||
resolutionLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[resolutionLabel.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[resolutionLabel.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset]
|
||
]];
|
||
topOffset += 20;
|
||
|
||
_resolutionSegment = [[UISegmentedControl alloc] initWithItems:@[@"360p", @"480p", @"540p", @"720p"]];
|
||
[contentView addSubview:_resolutionSegment];
|
||
_resolutionSegment.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[_resolutionSegment.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[_resolutionSegment.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor constant:-20],
|
||
[_resolutionSegment.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset],
|
||
[_resolutionSegment.heightAnchor constraintEqualToConstant:32]
|
||
]];
|
||
topOffset += 32 + spacing;
|
||
|
||
// FPS
|
||
UILabel *fpsLabel = [[UILabel alloc] init];
|
||
fpsLabel.text = @"帧率 (FPS)";
|
||
fpsLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||
[contentView addSubview:fpsLabel];
|
||
fpsLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[fpsLabel.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[fpsLabel.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset]
|
||
]];
|
||
topOffset += 20;
|
||
|
||
_fpsField = [[UITextField alloc] init];
|
||
_fpsField.placeholder = @"例如: 30";
|
||
_fpsField.borderStyle = UITextBorderStyleRoundedRect;
|
||
_fpsField.keyboardType = UIKeyboardTypeNumberPad;
|
||
_fpsField.delegate = self;
|
||
[self addDoneToolbarToTextField:_fpsField];
|
||
[contentView addSubview:_fpsField];
|
||
_fpsField.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[_fpsField.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[_fpsField.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor constant:-20],
|
||
[_fpsField.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset],
|
||
[_fpsField.heightAnchor constraintEqualToConstant:40]
|
||
]];
|
||
topOffset += 40 + spacing;
|
||
|
||
// Max Bitrate
|
||
UILabel *maxBitrateLabel = [[UILabel alloc] init];
|
||
maxBitrateLabel.text = @"最大码率 (kbps)";
|
||
maxBitrateLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||
[contentView addSubview:maxBitrateLabel];
|
||
maxBitrateLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[maxBitrateLabel.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[maxBitrateLabel.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset]
|
||
]];
|
||
topOffset += 20;
|
||
|
||
_maxBitrateField = [[UITextField alloc] init];
|
||
_maxBitrateField.placeholder = @"例如: 2000";
|
||
_maxBitrateField.borderStyle = UITextBorderStyleRoundedRect;
|
||
_maxBitrateField.keyboardType = UIKeyboardTypeNumberPad;
|
||
_maxBitrateField.delegate = self;
|
||
[self addDoneToolbarToTextField:_maxBitrateField];
|
||
[contentView addSubview:_maxBitrateField];
|
||
_maxBitrateField.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[_maxBitrateField.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[_maxBitrateField.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor constant:-20],
|
||
[_maxBitrateField.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset],
|
||
[_maxBitrateField.heightAnchor constraintEqualToConstant:40]
|
||
]];
|
||
topOffset += 40 + spacing;
|
||
|
||
// Min Bitrate
|
||
UILabel *minBitrateLabel = [[UILabel alloc] init];
|
||
minBitrateLabel.text = @"最小码率 (kbps)";
|
||
minBitrateLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||
[contentView addSubview:minBitrateLabel];
|
||
minBitrateLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[minBitrateLabel.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[minBitrateLabel.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset]
|
||
]];
|
||
topOffset += 20;
|
||
|
||
_minBitrateField = [[UITextField alloc] init];
|
||
_minBitrateField.placeholder = @"例如: 500";
|
||
_minBitrateField.borderStyle = UITextBorderStyleRoundedRect;
|
||
_minBitrateField.keyboardType = UIKeyboardTypeNumberPad;
|
||
_minBitrateField.delegate = self;
|
||
[self addDoneToolbarToTextField:_minBitrateField];
|
||
[contentView addSubview:_minBitrateField];
|
||
_minBitrateField.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[_minBitrateField.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor constant:20],
|
||
[_minBitrateField.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor constant:-20],
|
||
[_minBitrateField.topAnchor constraintEqualToAnchor:contentView.topAnchor constant:topOffset],
|
||
[_minBitrateField.heightAnchor constraintEqualToConstant:40]
|
||
]];
|
||
topOffset += 40 + spacing;
|
||
} // End of video parameters conditional block
|
||
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[contentView.heightAnchor constraintEqualToConstant:topOffset]
|
||
]];
|
||
|
||
// Buttons
|
||
UIButton *applyButton = [UIButton buttonWithType:UIButtonTypeSystem];
|
||
[applyButton setTitle:@"应用" forState:UIControlStateNormal];
|
||
applyButton.backgroundColor = [UIColor systemBlueColor];
|
||
[applyButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||
applyButton.layer.cornerRadius = 8;
|
||
[applyButton addTarget:self action:@selector(applyTapped) forControlEvents:UIControlEventTouchUpInside];
|
||
[_containerView addSubview:applyButton];
|
||
|
||
// 根据屏幕方向动态调整按钮高度和底部间距
|
||
CGFloat buttonHeight = isLandscape ? 36 : 44; // 横屏时按钮更矮
|
||
CGFloat buttonBottomMargin = isLandscape ? 12 : 20; // 横屏时减小底部边距
|
||
|
||
applyButton.translatesAutoresizingMaskIntoConstraints = NO;
|
||
[NSLayoutConstraint activateConstraints:@[
|
||
[applyButton.leadingAnchor constraintEqualToAnchor:_containerView.leadingAnchor constant:20],
|
||
[applyButton.trailingAnchor constraintEqualToAnchor:_containerView.trailingAnchor constant:-20],
|
||
[applyButton.bottomAnchor constraintEqualToAnchor:_containerView.bottomAnchor constant:-buttonBottomMargin],
|
||
[applyButton.heightAnchor constraintEqualToConstant:buttonHeight]
|
||
]];
|
||
|
||
// Tap to dismiss
|
||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundTapped:)];
|
||
[self addGestureRecognizer:tap];
|
||
}
|
||
|
||
- (void)showInViewController:(UIViewController *)viewController
|
||
withConfig:(AVVideoConfiguration *)config
|
||
fieldsMask:(AVSettingsFieldMask)fieldsMask
|
||
callback:(AVSettingsViewCallback)callback {
|
||
self.callback = callback;
|
||
self.tempConfig = [config copy];
|
||
self.fieldsMask = fieldsMask;
|
||
|
||
// Setup UI now that we have fieldsMask
|
||
[self setupUI];
|
||
|
||
// Load config values
|
||
if (_streamIdField) {
|
||
// Generate random streamId if empty or default
|
||
if (config.streamId.length == 0 || [config.streamId isEqualToString:@"stream"]) {
|
||
// Generate random 3-digit number (100-999)
|
||
NSInteger randomNumber = 100 + arc4random_uniform(900);
|
||
_streamIdField.text = [NSString stringWithFormat:@"stream%ld", (long)randomNumber];
|
||
// Update the temp config with the generated streamId
|
||
self.tempConfig.streamId = _streamIdField.text;
|
||
} else {
|
||
_streamIdField.text = config.streamId;
|
||
}
|
||
}
|
||
if (_nicknameField) {
|
||
_nicknameField.text = config.nickname;
|
||
}
|
||
if (_resolutionSegment) {
|
||
_resolutionSegment.selectedSegmentIndex = [config currentResolution];
|
||
}
|
||
if (_fpsField) {
|
||
_fpsField.text = [NSString stringWithFormat:@"%ld", (long)config.videoFrameRate];
|
||
}
|
||
if (_maxBitrateField) {
|
||
_maxBitrateField.text = [NSString stringWithFormat:@"%ld", (long)(config.videoBitRate / 1000)];
|
||
}
|
||
if (_minBitrateField) {
|
||
_minBitrateField.text = [NSString stringWithFormat:@"%ld", (long)(config.videoMinBitRate / 1000)];
|
||
}
|
||
|
||
self.frame = viewController.view.bounds;
|
||
self.alpha = 0;
|
||
[viewController.view addSubview:self];
|
||
|
||
[UIView animateWithDuration:0.3 animations:^{
|
||
self.alpha = 1.0;
|
||
}];
|
||
}
|
||
|
||
- (void)dismiss {
|
||
[UIView animateWithDuration:0.3 animations:^{
|
||
self.alpha = 0;
|
||
} completion:^(BOOL finished) {
|
||
[self removeFromSuperview];
|
||
}];
|
||
}
|
||
|
||
- (void)applyTapped {
|
||
// Update config with all field values
|
||
if (_streamIdField) {
|
||
self.tempConfig.streamId = _streamIdField.text.length > 0 ? _streamIdField.text : @"";
|
||
}
|
||
if (_nicknameField) {
|
||
self.tempConfig.nickname = _nicknameField.text.length > 0 ? _nicknameField.text : @"test";
|
||
}
|
||
if (_resolutionSegment) {
|
||
[self.tempConfig setResolution:_resolutionSegment.selectedSegmentIndex];
|
||
}
|
||
if (_fpsField) {
|
||
NSInteger fps = [_fpsField.text integerValue];
|
||
// Validate FPS
|
||
if (fps <= 0) {
|
||
fps = 30;
|
||
}
|
||
self.tempConfig.videoFrameRate = fps;
|
||
self.tempConfig.videoMaxKeyframeInterval = fps * 2;
|
||
}
|
||
if (_maxBitrateField) {
|
||
NSInteger maxBitrate = [_maxBitrateField.text integerValue];
|
||
// Validate max bitrate
|
||
if (maxBitrate <= 0) {
|
||
maxBitrate = 2000;
|
||
}
|
||
self.tempConfig.videoBitRate = maxBitrate * 1000; // Convert kbps to bps
|
||
}
|
||
if (_minBitrateField) {
|
||
NSInteger minBitrate = [_minBitrateField.text integerValue];
|
||
// Validate min bitrate
|
||
if (minBitrate <= 0) {
|
||
minBitrate = 500;
|
||
}
|
||
self.tempConfig.videoMinBitRate = minBitrate * 1000; // Convert kbps to bps
|
||
}
|
||
|
||
if (self.callback) {
|
||
self.callback(self.tempConfig);
|
||
}
|
||
|
||
[self dismiss];
|
||
}
|
||
|
||
- (void)backgroundTapped:(UITapGestureRecognizer *)recognizer {
|
||
CGPoint location = [recognizer locationInView:self];
|
||
if (!CGRectContainsPoint(_containerView.frame, location)) {
|
||
// 点击背景关闭弹窗
|
||
[self dismiss];
|
||
} else {
|
||
// 点击容器内部,收起键盘
|
||
[self endEditing:YES];
|
||
}
|
||
}
|
||
|
||
#pragma mark - Keyboard Handling
|
||
|
||
- (void)keyboardWillShow:(NSNotification *)notification {
|
||
NSDictionary *userInfo = notification.userInfo;
|
||
CGRect keyboardFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
||
CGFloat keyboardHeight = keyboardFrame.size.height;
|
||
NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
|
||
UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
|
||
|
||
// 方案:调整 ScrollView 的 contentInset,而不是移动整个容器
|
||
// 计算键盘遮挡的高度
|
||
CGRect containerFrameInWindow = [_containerView convertRect:_containerView.bounds toView:self];
|
||
CGFloat containerBottom = CGRectGetMaxY(containerFrameInWindow);
|
||
CGFloat keyboardTop = CGRectGetHeight(self.bounds) - keyboardHeight;
|
||
CGFloat overlap = containerBottom - keyboardTop;
|
||
|
||
if (overlap > 0) {
|
||
// 键盘会遮挡容器,调整 scrollView 的 contentInset
|
||
UIEdgeInsets contentInset = _scrollView.contentInset;
|
||
contentInset.bottom = overlap + 20; // 额外留20px边距
|
||
|
||
[UIView animateWithDuration:duration delay:0 options:(curve << 16) animations:^{
|
||
self.scrollView.contentInset = contentInset;
|
||
self.scrollView.scrollIndicatorInsets = contentInset;
|
||
|
||
// 如果有激活的输入框,滚动到可见位置
|
||
if (self.activeTextField) {
|
||
[self scrollToTextField:self.activeTextField];
|
||
}
|
||
} completion:nil];
|
||
}
|
||
}
|
||
|
||
- (void)keyboardWillHide:(NSNotification *)notification {
|
||
NSDictionary *userInfo = notification.userInfo;
|
||
NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
|
||
UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
|
||
|
||
// 恢复 scrollView 的 contentInset
|
||
[UIView animateWithDuration:duration delay:0 options:(curve << 16) animations:^{
|
||
self.scrollView.contentInset = UIEdgeInsetsZero;
|
||
self.scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
|
||
} completion:nil];
|
||
}
|
||
|
||
- (void)scrollToTextField:(UITextField *)textField {
|
||
// 计算 textField 在 scrollView 中的位置
|
||
CGRect textFieldFrame = [textField convertRect:textField.bounds toView:_contentView];
|
||
|
||
// 添加一些边距,确保输入框上下都有空间
|
||
CGFloat padding = 20;
|
||
CGRect targetRect = CGRectMake(textFieldFrame.origin.x,
|
||
textFieldFrame.origin.y - padding,
|
||
textFieldFrame.size.width,
|
||
textFieldFrame.size.height + padding * 2);
|
||
|
||
// 滚动到目标位置
|
||
[_scrollView scrollRectToVisible:targetRect animated:YES];
|
||
}
|
||
|
||
#pragma mark - Helper Methods
|
||
|
||
- (void)addDoneToolbarToTextField:(UITextField *)textField {
|
||
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), 44)];
|
||
toolbar.barStyle = UIBarStyleDefault;
|
||
|
||
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
|
||
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneButtonTapped)];
|
||
|
||
toolbar.items = @[flexSpace, doneButton];
|
||
textField.inputAccessoryView = toolbar;
|
||
}
|
||
|
||
- (void)doneButtonTapped {
|
||
[self endEditing:YES];
|
||
}
|
||
|
||
#pragma mark - UITextFieldDelegate
|
||
|
||
- (void)textFieldDidBeginEditing:(UITextField *)textField {
|
||
self.activeTextField = textField;
|
||
|
||
// 延迟一点点,等键盘动画开始
|
||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||
[self scrollToTextField:textField];
|
||
});
|
||
}
|
||
|
||
- (void)textFieldDidEndEditing:(UITextField *)textField {
|
||
if (self.activeTextField == textField) {
|
||
self.activeTextField = nil;
|
||
}
|
||
}
|
||
|
||
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
||
[textField resignFirstResponder];
|
||
return YES;
|
||
}
|
||
|
||
@end
|