359 lines
16 KiB
Objective-C
359 lines
16 KiB
Objective-C
//
|
||
// SellyCallControlView.m
|
||
// SellyCloudSDK_Example
|
||
//
|
||
// Created by Caleb on 12/17/25.
|
||
// Copyright © 2025 Caleb. All rights reserved.
|
||
//
|
||
|
||
#import "SellyCallControlView.h"
|
||
#import <Masonry/Masonry.h>
|
||
|
||
@interface SellyCallControlView ()
|
||
|
||
@property (nonatomic, strong) UIButton *speakerButton;
|
||
@property (nonatomic, strong) UIButton *videoButton;
|
||
@property (nonatomic, strong) UIButton *switchCameraButton;
|
||
@property (nonatomic, strong) UIButton *muteButton;
|
||
@property (nonatomic, strong) UIButton *pipButton;
|
||
@property (nonatomic, strong) UIButton *screenShareButton;
|
||
@property (nonatomic, strong) UIButton *hangupButton;
|
||
|
||
@property (nonatomic, strong) UIStackView *mainStackView; // 主垂直容器
|
||
@property (nonatomic, strong) UIStackView *topButtonStackView; // 上排按钮
|
||
@property (nonatomic, strong) UIStackView *bottomButtonStackView; // 下排按钮
|
||
|
||
@end
|
||
|
||
@implementation SellyCallControlView
|
||
|
||
- (instancetype)initWithFrame:(CGRect)frame {
|
||
self = [super initWithFrame:frame];
|
||
if (self) {
|
||
[self setupUI];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (instancetype)initWithCoder:(NSCoder *)coder {
|
||
self = [super initWithCoder:coder];
|
||
if (self) {
|
||
[self setupUI];
|
||
}
|
||
return self;
|
||
}
|
||
|
||
- (void)setupUI {
|
||
// 设置透明背景
|
||
self.backgroundColor = [UIColor clearColor];
|
||
|
||
// 创建主垂直容器
|
||
self.mainStackView = [[UIStackView alloc] init];
|
||
self.mainStackView.axis = UILayoutConstraintAxisVertical;
|
||
self.mainStackView.distribution = UIStackViewDistributionFillEqually;
|
||
self.mainStackView.alignment = UIStackViewAlignmentFill;
|
||
self.mainStackView.spacing = 10;
|
||
[self addSubview:self.mainStackView];
|
||
|
||
// 创建上排按钮容器
|
||
self.topButtonStackView = [[UIStackView alloc] init];
|
||
self.topButtonStackView.axis = UILayoutConstraintAxisHorizontal;
|
||
self.topButtonStackView.distribution = UIStackViewDistributionEqualSpacing;
|
||
self.topButtonStackView.alignment = UIStackViewAlignmentCenter;
|
||
self.topButtonStackView.spacing = 0;
|
||
|
||
// 创建下排按钮容器
|
||
self.bottomButtonStackView = [[UIStackView alloc] init];
|
||
self.bottomButtonStackView.axis = UILayoutConstraintAxisHorizontal;
|
||
self.bottomButtonStackView.distribution = UIStackViewDistributionEqualSpacing;
|
||
self.bottomButtonStackView.alignment = UIStackViewAlignmentCenter;
|
||
self.bottomButtonStackView.spacing = 0;
|
||
|
||
[self.mainStackView addArrangedSubview:self.topButtonStackView];
|
||
[self.mainStackView addArrangedSubview:self.bottomButtonStackView];
|
||
|
||
// 创建按钮
|
||
self.speakerButton = [self createButtonWithImageName:@"speaker" title:@"扬声器" action:@selector(speakerButtonTapped:)];
|
||
self.videoButton = [self createButtonWithImageName:@"video" title:@"视频" action:@selector(videoButtonTapped:)];
|
||
self.switchCameraButton = [self createButtonWithImageName:@"switch_camera" title:@"切换" action:@selector(switchCameraButtonTapped:)];
|
||
self.muteButton = [self createButtonWithImageName:@"microphone" title:@"静音" action:@selector(muteButtonTapped:)];
|
||
self.pipButton = [self createButtonWithImageName:@"pip" title:@"画中画" action:@selector(pipButtonTapped:)];
|
||
self.screenShareButton = [self createButtonWithImageName:@"screen_share" title:@"共享屏幕" action:@selector(screenShareButtonTapped:)];
|
||
self.hangupButton = [self createButtonWithImageName:@"hangup" title:@"挂断" action:@selector(hangupButtonTapped:)];
|
||
|
||
// 挂断按钮的图标背景设置为红色
|
||
UIView *hangupIconBackground = [self.hangupButton viewWithTag:999];
|
||
if (hangupIconBackground) {
|
||
hangupIconBackground.backgroundColor = [UIColor colorWithRed:0.9 green:0.2 blue:0.2 alpha:1.0];
|
||
}
|
||
|
||
// 添加按钮到上排容器(4个按钮)
|
||
[self.topButtonStackView addArrangedSubview:self.speakerButton];
|
||
[self.topButtonStackView addArrangedSubview:self.videoButton];
|
||
[self.topButtonStackView addArrangedSubview:self.switchCameraButton];
|
||
[self.topButtonStackView addArrangedSubview:self.muteButton];
|
||
|
||
// 添加按钮到下排容器(根据配置显示 PiP 或屏幕分享)
|
||
// 默认不显示 PiP 和屏幕分享按钮,由外部控制
|
||
self.pipButton.hidden = YES;
|
||
self.screenShareButton.hidden = YES;
|
||
[self.bottomButtonStackView addArrangedSubview:self.pipButton];
|
||
[self.bottomButtonStackView addArrangedSubview:self.screenShareButton];
|
||
[self.bottomButtonStackView addArrangedSubview:self.hangupButton];
|
||
|
||
// 布局约束 - 使用 Masonry
|
||
[self.mainStackView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.leading.equalTo(self).offset(15);
|
||
make.trailing.equalTo(self).offset(-15);
|
||
make.top.equalTo(self).offset(10);
|
||
make.bottom.equalTo(self).offset(-10);
|
||
}];
|
||
|
||
// 上排容器内边距(让按钮居中分布)
|
||
[self.topButtonStackView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.leading.trailing.equalTo(self.mainStackView);
|
||
}];
|
||
|
||
// 下排容器内边距(让2个按钮合理分布,不要太分散)
|
||
[self.bottomButtonStackView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.centerX.equalTo(self.mainStackView);
|
||
make.width.mas_lessThanOrEqualTo(self.mainStackView);
|
||
}];
|
||
}
|
||
|
||
- (UIButton *)createButtonWithImageName:(NSString *)imageName title:(NSString *)title action:(SEL)action {
|
||
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
|
||
|
||
// 创建容器视图
|
||
UIView *containerView = [[UIView alloc] init];
|
||
containerView.userInteractionEnabled = NO;
|
||
[button addSubview:containerView];
|
||
|
||
// 图标背景
|
||
UIView *iconBackground = [[UIView alloc] init];
|
||
iconBackground.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2];
|
||
iconBackground.layer.cornerRadius = 26; // 52/2 = 26
|
||
iconBackground.clipsToBounds = YES;
|
||
iconBackground.userInteractionEnabled = NO;
|
||
iconBackground.tag = 999; // 添加tag以便后续识别
|
||
[containerView addSubview:iconBackground];
|
||
|
||
// 使用系统图标
|
||
UIImage *icon = [self systemImageForButtonName:imageName];
|
||
UIImageView *imageView = [[UIImageView alloc] initWithImage:icon];
|
||
imageView.contentMode = UIViewContentModeScaleAspectFit;
|
||
imageView.tintColor = [UIColor whiteColor];
|
||
imageView.userInteractionEnabled = NO;
|
||
[iconBackground addSubview:imageView];
|
||
|
||
// 标题标签
|
||
UILabel *titleLabel = [[UILabel alloc] init];
|
||
titleLabel.text = title;
|
||
titleLabel.textColor = [UIColor whiteColor];
|
||
titleLabel.font = [UIFont systemFontOfSize:12];
|
||
titleLabel.textAlignment = NSTextAlignmentCenter;
|
||
titleLabel.userInteractionEnabled = NO;
|
||
[containerView addSubview:titleLabel];
|
||
|
||
// 使用 Masonry 布局
|
||
[containerView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.center.equalTo(button);
|
||
make.width.mas_equalTo(70);
|
||
make.height.mas_equalTo(90);
|
||
}];
|
||
|
||
[iconBackground mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.centerX.equalTo(containerView);
|
||
make.top.equalTo(containerView);
|
||
make.width.height.mas_equalTo(52);
|
||
}];
|
||
|
||
[imageView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.center.equalTo(iconBackground);
|
||
make.width.height.mas_equalTo(28);
|
||
}];
|
||
|
||
[titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.top.equalTo(iconBackground.mas_bottom).offset(8);
|
||
make.centerX.equalTo(containerView);
|
||
make.leading.trailing.equalTo(containerView);
|
||
}];
|
||
|
||
[button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
|
||
|
||
// 设置按钮尺寸
|
||
[button mas_makeConstraints:^(MASConstraintMaker *make) {
|
||
make.width.mas_equalTo(70);
|
||
make.height.mas_equalTo(90);
|
||
}];
|
||
|
||
return button;
|
||
}
|
||
|
||
- (UIImage *)systemImageForButtonName:(NSString *)name {
|
||
NSString *systemName = @"questionmark.circle";
|
||
|
||
if ([name isEqualToString:@"speaker"]) {
|
||
systemName = @"speaker.wave.2.fill";
|
||
} else if ([name isEqualToString:@"video"]) {
|
||
systemName = @"video.fill";
|
||
} else if ([name isEqualToString:@"switch_camera"]) {
|
||
systemName = @"arrow.triangle.2.circlepath.camera.fill";
|
||
} else if ([name isEqualToString:@"microphone"]) {
|
||
systemName = @"mic.fill";
|
||
} else if ([name isEqualToString:@"pip"]) {
|
||
systemName = @"pip.fill";
|
||
} else if ([name isEqualToString:@"screen_share"]) {
|
||
systemName = @"rectangle.on.rectangle";
|
||
} else if ([name isEqualToString:@"hangup"]) {
|
||
systemName = @"phone.down.fill";
|
||
}
|
||
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
return [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
}
|
||
|
||
#pragma mark - Button Actions
|
||
|
||
- (void)speakerButtonTapped:(UIButton *)sender {
|
||
if ([self.delegate respondsToSelector:@selector(callControlView:didTapAction:)]) {
|
||
[self.delegate callControlView:self didTapAction:SellyCallControlActionSpeaker];
|
||
}
|
||
}
|
||
|
||
- (void)videoButtonTapped:(UIButton *)sender {
|
||
if ([self.delegate respondsToSelector:@selector(callControlView:didTapAction:)]) {
|
||
[self.delegate callControlView:self didTapAction:SellyCallControlActionVideo];
|
||
}
|
||
}
|
||
|
||
- (void)switchCameraButtonTapped:(UIButton *)sender {
|
||
if ([self.delegate respondsToSelector:@selector(callControlView:didTapAction:)]) {
|
||
[self.delegate callControlView:self didTapAction:SellyCallControlActionSwitchCamera];
|
||
}
|
||
}
|
||
|
||
- (void)muteButtonTapped:(UIButton *)sender {
|
||
if ([self.delegate respondsToSelector:@selector(callControlView:didTapAction:)]) {
|
||
[self.delegate callControlView:self didTapAction:SellyCallControlActionMute];
|
||
}
|
||
}
|
||
|
||
- (void)hangupButtonTapped:(UIButton *)sender {
|
||
if ([self.delegate respondsToSelector:@selector(callControlView:didTapAction:)]) {
|
||
[self.delegate callControlView:self didTapAction:SellyCallControlActionHangup];
|
||
}
|
||
}
|
||
|
||
- (void)pipButtonTapped:(UIButton *)sender {
|
||
if ([self.delegate respondsToSelector:@selector(callControlView:didTapAction:)]) {
|
||
[self.delegate callControlView:self didTapAction:SellyCallControlActionPiP];
|
||
}
|
||
}
|
||
|
||
- (void)screenShareButtonTapped:(UIButton *)sender {
|
||
if ([self.delegate respondsToSelector:@selector(callControlView:didTapAction:)]) {
|
||
[self.delegate callControlView:self didTapAction:SellyCallControlActionScreenShare];
|
||
}
|
||
}
|
||
|
||
#pragma mark - Public Methods
|
||
|
||
- (void)setShowPiPButton:(BOOL)showPiPButton {
|
||
_showPiPButton = showPiPButton;
|
||
self.pipButton.hidden = !showPiPButton;
|
||
}
|
||
|
||
- (void)setShowScreenShareButton:(BOOL)showScreenShareButton {
|
||
_showScreenShareButton = showScreenShareButton;
|
||
self.screenShareButton.hidden = !showScreenShareButton;
|
||
}
|
||
|
||
- (void)updateSpeakerEnabled:(BOOL)enabled {
|
||
// 更新图标和背景
|
||
UIView *iconBackground = self.speakerButton.subviews.firstObject.subviews.firstObject;
|
||
if (enabled) {
|
||
iconBackground.backgroundColor = [UIColor colorWithRed:0.2 green:0.5 blue:1.0 alpha:1.0];
|
||
NSString *systemName = @"speaker.wave.2.fill";
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
UIImageView *imageView = (UIImageView *)iconBackground.subviews.firstObject;
|
||
imageView.image = [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
} else {
|
||
iconBackground.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2];
|
||
NSString *systemName = @"speaker.fill";
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
UIImageView *imageView = (UIImageView *)iconBackground.subviews.firstObject;
|
||
imageView.image = [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
}
|
||
}
|
||
|
||
- (void)updateVideoEnabled:(BOOL)enabled {
|
||
UIView *iconBackground = self.videoButton.subviews.firstObject.subviews.firstObject;
|
||
if (enabled) {
|
||
iconBackground.backgroundColor = [UIColor colorWithRed:0.2 green:0.5 blue:1.0 alpha:1.0];
|
||
NSString *systemName = @"video.fill";
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
UIImageView *imageView = (UIImageView *)iconBackground.subviews.firstObject;
|
||
imageView.image = [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
} else {
|
||
iconBackground.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2];
|
||
NSString *systemName = @"video.slash.fill";
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
UIImageView *imageView = (UIImageView *)iconBackground.subviews.firstObject;
|
||
imageView.image = [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
}
|
||
}
|
||
|
||
- (void)updateMuteEnabled:(BOOL)muted {
|
||
UIView *iconBackground = self.muteButton.subviews.firstObject.subviews.firstObject;
|
||
if (muted) {
|
||
iconBackground.backgroundColor = [UIColor colorWithRed:0.9 green:0.2 blue:0.2 alpha:1.0];
|
||
NSString *systemName = @"mic.slash.fill";
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
UIImageView *imageView = (UIImageView *)iconBackground.subviews.firstObject;
|
||
imageView.image = [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
} else {
|
||
iconBackground.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2];
|
||
NSString *systemName = @"mic.fill";
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
UIImageView *imageView = (UIImageView *)iconBackground.subviews.firstObject;
|
||
imageView.image = [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
}
|
||
}
|
||
|
||
- (void)updatePiPEnabled:(BOOL)enabled {
|
||
UIView *iconBackground = self.pipButton.subviews.firstObject.subviews.firstObject;
|
||
if (enabled) {
|
||
iconBackground.backgroundColor = [UIColor colorWithRed:0.2 green:0.5 blue:1.0 alpha:1.0];
|
||
NSString *systemName = @"pip.fill";
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
UIImageView *imageView = (UIImageView *)iconBackground.subviews.firstObject;
|
||
imageView.image = [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
} else {
|
||
iconBackground.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2];
|
||
NSString *systemName = @"pip";
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
UIImageView *imageView = (UIImageView *)iconBackground.subviews.firstObject;
|
||
imageView.image = [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
}
|
||
}
|
||
|
||
- (void)updateScreenShareEnabled:(BOOL)enabled {
|
||
UIView *iconBackground = self.screenShareButton.subviews.firstObject.subviews.firstObject;
|
||
if (enabled) {
|
||
iconBackground.backgroundColor = [UIColor colorWithRed:0.2 green:0.5 blue:1.0 alpha:1.0];
|
||
NSString *systemName = @"rectangle.on.rectangle.fill";
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
UIImageView *imageView = (UIImageView *)iconBackground.subviews.firstObject;
|
||
imageView.image = [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
} else {
|
||
iconBackground.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.2];
|
||
NSString *systemName = @"rectangle.on.rectangle";
|
||
UIImageSymbolConfiguration *config = [UIImageSymbolConfiguration configurationWithPointSize:28 weight:UIImageSymbolWeightMedium scale:UIImageSymbolScaleMedium];
|
||
UIImageView *imageView = (UIImageView *)iconBackground.subviews.firstObject;
|
||
imageView.image = [UIImage systemImageNamed:systemName withConfiguration:config];
|
||
}
|
||
}
|
||
|
||
@end
|