Files
SellyCloudSDK_demo/Example/SellyCloudSDK/VideoCall/SellyCallControlView.m
2026-03-01 15:59:27 +08:00

359 lines
16 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// 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