initial commit

This commit is contained in:
Caleb
2026-03-01 15:59:27 +08:00
commit a9e97d56cb
1426 changed files with 172367 additions and 0 deletions

View File

@@ -0,0 +1,180 @@
//
// 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/21/31/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