SellyCloudSDK_demo/Example/SellyCloudSDK/VideoCall/SLSVideoGridView.m

181 lines
4.6 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.

//
// SLSVideoGridView.m
// SellyCloudSDK_Example
//
// Created by Caleb on 13/11/25.
// Copyright © 2025 Caleb. All rights reserved.
//
#import "SLSVideoGridView.h"
@interface SLSVideoGridView ()
@property (nonatomic, strong) NSMutableDictionary<NSString*, SLSParticipant*> *participants;
@property (nonatomic, strong) NSMutableDictionary<NSString*, SLSVideoTileView*> *tiles;
@end
@implementation SLSVideoGridView
// 代码创建时走这里
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self commonInit];
}
return self;
}
// xib / storyboard 反序列化时走这里
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self commonInit];
}
return self;
}
// 公共初始化逻辑
- (void)commonInit {
_participants = [NSMutableDictionary dictionary];
_tiles = [NSMutableDictionary dictionary];
_spacing = 8.f;
_padding = UIEdgeInsetsMake(8, 8, 8, 8);
_keepAspect169 = YES;
self.backgroundColor = [UIColor blackColor];
}
#pragma mark - Public
- (SLSVideoTileView *)ensureRenderContainerForUID:(NSString *)uid
displayName:(nullable NSString *)name {
if (!uid) return nil;
SLSParticipant *p = self.participants[uid];
if (!p) {
p = [SLSParticipant new];
p.uid = uid;
p.displayName = name ?: uid;
p.lastActiveAt = [NSDate date];
self.participants[uid] = p;
}
SLSVideoTileView *tile = self.tiles[uid];
if (!tile) {
tile = [[SLSVideoTileView alloc] initWithFrame:CGRectZero];
self.tiles[uid] = tile;
[self addSubview:tile];
}
[tile updateWithParticipant:p];
[self _layout];
return tile;
}
- (void)detachUID:(NSString *)uid {
if (!uid) return;
SLSVideoTileView *tile = self.tiles[uid];
if (tile) {
[tile removeFromSuperview];
[self.tiles removeObjectForKey:uid];
}
[self.participants removeObjectForKey:uid];
[self _layout];
}
- (void)setLevel:(CGFloat)level forUID:(NSString *)uid {
SLSParticipant *p = self.participants[uid];
if (!p) return;
p.level = MAX(0.f, MIN(level, 1.f));
if (p.level > 0.1) {
p.lastActiveAt = [NSDate date];
}
SLSVideoTileView *tile = self.tiles[uid];
[tile updateWithParticipant:p];
}
- (void)setAudioMuted:(BOOL)muted forUID:(NSString *)uid {
SLSParticipant *p = self.participants[uid];
if (!p) return;
p.audioMuted = muted;
SLSVideoTileView *tile = self.tiles[uid];
[tile updateWithParticipant:p];
}
- (void)setVideoMuted:(BOOL)muted forUID:(NSString *)uid {
SLSParticipant *p = self.participants[uid];
if (!p) return;
p.videoMuted = muted;
SLSVideoTileView *tile = self.tiles[uid];
[tile updateWithParticipant:p];
}
#pragma mark - Layout
- (void)_layout {
NSArray<NSString *> *uids = self.participants.allKeys;
NSInteger n = uids.count;
if (n == 0) return;
UIEdgeInsets p = self.padding;
CGFloat W = self.bounds.size.width - p.left - p.right;
CGFloat H = self.bounds.size.height - p.top - p.bottom;
CGFloat s = self.spacing;
// ===== 按人数选择固定的行列数 =====
NSInteger cols = 1;
NSInteger rows = 1;
if (n <= 4) {
cols = 2;
rows = 2;
} else if (n <= 9) {
cols = 3;
rows = 3;
} else if (n <= 16) {
cols = 4;
rows = 4;
} else {
// >16 的情况先简单处理:依旧按 4x4 布局,多出来的不显示
cols = 4;
rows = 4;
}
// 每个 cell 的宽 / 高(都按“可用区域的 1/2、1/3、1/4”来扣掉间距
CGFloat cellW = 0;
CGFloat cellH = 0;
if (cols > 0) {
cellW = floor((W - s * (cols - 1)) / cols);
}
if (rows > 0) {
cellH = floor((H - s * (rows - 1)) / rows);
}
NSInteger maxCells = cols * rows;
NSInteger countToLayout = MIN(n, maxCells);
[uids enumerateObjectsUsingBlock:^(NSString *uid, NSUInteger idx, BOOL *stop) {
if (idx >= countToLayout) {
*stop = YES;
return;
}
NSInteger r = idx / cols; // 第几行
NSInteger c = idx % cols; // 第几列
CGFloat x = p.left + c * (cellW + s);
CGFloat y = p.top + r * (cellH + s);
SLSVideoTileView *tile = self.tiles[uid];
tile.frame = CGRectMake(x, y, cellW, cellH);
SLSParticipant *part = self.participants[uid];
[tile updateWithParticipant:part];
}];
}
- (void)layoutSubviews {
[super layoutSubviews];
[self _layout];
}
@end