插槽系统(Slot System)是AliPlayerKit的核心架构设计,通过组件化与可插拔机制,将播放器UI拆分为多个独立的插槽组件,实现界面的解耦、组合与灵活扩展。
概念介绍
什么是插槽系统?
插槽系统 (Slot System) 是统一管理插槽的架构机制,负责插槽的注册、生命周期、场景适配、渲染顺序及运行时状态控制。基于该机制,开发者可灵活使用默认插槽快速搭建 UI,或按需替换、扩展乃至完全自定义组件,实现高度定制化的播放器界面。
什么是插槽?
插槽 (Slot) 是播放器 UI 的基本组成单元,代表独立的界面组件(如顶部控制栏、底部进度条、封面图等)。所有插槽按预定义 Z-Order 叠加构成完整界面,各自仅关注自身的展示与交互逻辑,实现职责清晰、粒度可控的结构。
核心价值
插槽系统将播放器UI组件独立拆解,开发者可自主选择使用方式。
使用方式 | 说明 | 优势 |
使用默认界面 | 零配置,开箱即用 | 简化接入流程,低代码接入。 |
自定义部分插槽 | 只替换需要定制的插槽,其他保持默认 | 局部定制,保留默认交互。 |
完全自定义界面 | 替换所有插槽实现 | 满足品牌专属 UI 需求。 |
架构优势:
解耦:UI组件与播放器核心逻辑分离,职责清晰。
灵活:运行时动态组合、替换UI组件。
可扩展:无需修改框架即可自定义UI,扩展性强。
关注点分离:UI样式(XML布局)与业务逻辑(Slot类处理)分离。
功能特性
核心能力
能力 | 说明 |
动态组合 | 运行时自由组合 UI 组件。 |
热替换 | 无需重启即可替换组件实现。 |
场景适配 | 不同播放场景自动切换 UI 行为。 |
双生命周期 | View 生命周期与 Data 生命周期独立管理。 |
内置插槽类型
AliPlayerKit 内置 11 种插槽类型,按 Z-Order 从底到顶排列:
插槽类型 | 枚举值 | 说明 | 默认实现 |
视频画面 |
| 显示播放器视频内容(最底层) | PlayerSurfaceSlot |
手势控制 |
| 处理单击/双击/长按/拖动 | GestureControlSlot |
横屏提示 |
| 横屏视频在竖屏时提示全屏观看 | LandscapeHintSlot |
封面图 |
| 播放前显示视频封面 | CoverSlot |
中心显示 |
| 显示倍速/亮度/音量等状态提示 | CenterDisplaySlot |
播放状态 |
| 显示加载中/错误/播放完成等 | PlayStateSlot |
日志面板 |
| 调试时显示播放器日志 | LogPanelSlot |
顶部控制栏 |
| 返回按钮/标题/设置按钮 | TopBarSlot |
底部控制栏 |
| 播放暂停/进度条/全屏按钮 | BottomBarSlot |
设置菜单 |
| 倍速/清晰度/镜像/旋转 | SettingMenuSlot |
选项面板 |
| 倍速/清晰度选项弹出面板(最顶层) | OptionPanelSlot |
枚举值间隔为 10,以便后续插入新类型时不影响现有排序。枚举值同时决定了插槽的 Z 轴顺序(值越小越靠下)。通过 SlotTypeName() 函数可获取插槽类型的可读名称,SlotTypeAllBuiltinTypes() 函数可获取所有内置类型的有序数组。
Z-Order层级示意
┌────────────────────────────────────────┐ ← 最顶层
│ OptionPanel (选项面板) │ Z=110
├────────────────────────────────────────┤
│ SettingMenu (设置菜单) │ Z=100
├────────────────────────────────────────┤
│ BottomBar (底部控制栏) │ Z=90
├────────────────────────────────────────┤
│ TopBar (顶部控制栏) │ Z=80
├────────────────────────────────────────┤
│ LogPanel (日志面板) │ Z=70
├────────────────────────────────────────┤
│ PlayState (播放状态) │ Z=60
├────────────────────────────────────────┤
│ CenterDisplay (中心显示) │ Z=50
├────────────────────────────────────────┤
│ Cover (封面图) │ Z=40
├────────────────────────────────────────┤
│ LandscapeHint (横屏提示) │ Z=30
├────────────────────────────────────────┤
│ GestureControl (手势控制) │ Z=20
├────────────────────────────────────────┤
│ PlayerSurface (视频画面) │ Z=10
└────────────────────────────────────────┘ ← 最底层场景适配
AliPlayerKit 定义了 5 种播放场景,不同场景下插槽的可见性和行为自动适配。场景适配通过 SlotConfig 实现,DefaultSlotFactory 预置了所有内置插槽的默认配置。
场景 | 枚举值 | 说明 | 典型应用 |
VOD |
| 点播场景,支持所有功能 | 常规视频播放 |
LIVE |
| 直播场景,禁用时间轴操作 | 实时直播流 |
VIDEO_LIST |
| 列表播放场景,禁用垂直手势 | 信息流、短视频列表 |
RESTRICTED |
| 受限播放场景,限制跳跃播放 | 教育培训、考试监控 |
MINIMAL |
| 最小化播放场景,仅显示视频画面 | 背景视频、装饰视频 |
插槽可见性规则(由 DefaultSlotFactory.defaultConfigs 定义):
插槽 | VOD | LIVE | VIDEO_LIST | RESTRICTED | MINIMAL |
PlayerSurface | ✓ | ✓ | ✓ | ✓ | ✓ |
GestureControl | ✓ | ✓ | ✓ | ✓ | ✗ |
LandscapeHint | ✓ | ✓ | ✓ | ✓ | ✗ |
Cover | ✓ | ✗ | ✓ | ✗ | ✗ |
CenterDisplay | ✓ | ✓ | ✓ | ✓ | ✗ |
PlayState | ✓ | ✓ | ✓ | ✓ | ✓ |
LogPanel | 可配置 | 可配置 | 可配置 | 可配置 | ✗ |
TopBar | ✓ | ✓ | ✓ | ✓ | ✗ |
BottomBar | ✓ | ✓ | ✓ | ✓ | ✗ |
SettingMenu | ✓ | ✓ | ✓ | ✓ | ✗ |
OptionPanel | ✓ | ✓ | ✓ | ✓ | ✗ |
手势行为差异:
手势 | VOD | LIVE | VIDEO_LIST | RESTRICTED | MINIMAL |
单击 | 显示/隐藏控制栏 | 显示/隐藏控制栏 | 显示/隐藏控制栏 | 显示/隐藏控制栏 | 禁用 |
双击 | 切换播放/暂停 | 切换播放/暂停 | 切换播放/暂停 | 切换播放/暂停 | 禁用 |
长按 | 2倍速播放 | 禁用 | 2倍速播放 | 禁用 | 禁用 |
水平拖动 | 进度跳转 | 禁用 | 进度跳转 | 禁用 | 禁用 |
左侧垂直拖动 | 亮度调节 | 亮度调节 | 禁用 | 亮度调节 | 禁用 |
右侧垂直拖动 | 音量调节 | 音量调节 | 禁用 | 音量调节 | 禁用 |
使用方式
策略一:使用默认界面
最简单的使用方式,零配置开箱即用:
@implementation PlayerViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 创建播放器视图
AliPlayerView *playerView = [[AliPlayerView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:playerView];
// 2. 创建控制器
AliPlayerController *controller = [[AliPlayerController alloc] init];
// 3. 创建数据模型
AliPlayerModel *model = [[AliPlayerModel alloc] initWithVideoSource:videoSource];
model.sceneType = SceneTypeVOD;
model.autoPlay = YES;
// 4. 配置并绑定(自动使用默认插槽)
[controller configure:model];
[playerView attach:controller];
}
@end策略二:自定义部分插槽
只替换需要定制的插槽,其他使用默认实现:
// 1. 获取播放器视图的 SlotManager
SlotManager *slotManager = playerView.slotManager;
// 2. 只注册需要自定义的插槽(其他保持默认)
[slotManager registerSlotType:SlotTypeTopBar provider:^UIView<SlotProtocol> *(UIView *parentView) {
return [[MyCustomTopBarSlot alloc] initWithFrame:parentView.bounds];
}];
// 3. 配置并绑定
[controller configure:model];
[playerView attach:controller];策略三:完全自定义界面
清除所有默认配置,逐一注册自定义插槽:
// 1. 获取 SlotManager 并清除默认配置
SlotManager *slotManager = playerView.slotManager;
[slotManager clearAll];
// 2. 注册所有需要的自定义插槽
[slotManager registerSlotType:SlotTypePlayerSurface provider:^UIView<SlotProtocol> *(UIView *parentView) {
return [[MySurfaceSlot alloc] initWithFrame:parentView.bounds];
}];
[slotManager registerSlotType:SlotTypeCover provider:^UIView<SlotProtocol> *(UIView *parentView) {
return [[MyCoverSlot alloc] initWithFrame:parentView.bounds];
}];
// ... 其他插槽
// 3. 绑定
[controller configure:model];
[playerView attach:controller];插槽可见性控制
不替换插槽实现,仅控制显示/隐藏:
SlotManager *slotManager = playerView.slotManager;
// 隐藏整个插槽(rebuildSlots 时不构建)
[slotManager hideSlot:SlotTypeLogPanel];
// 显示之前隐藏的插槽(需调用 rebuildSlots 生效)
[slotManager showSlot:SlotTypeLogPanel];
[slotManager rebuildSlots];
// 检查插槽是否被隐藏
BOOL hidden = [slotManager isSlotHidden:SlotTypeLogPanel];元素级可见性控制
插槽系统支持对插槽内部的单个 UI 元素进行细粒度可见性控制,通过位掩码(SlotElements.h)实现:
SlotManager *slotManager = playerView.slotManager;
// 隐藏顶部栏中的下载按钮和截图按钮
[slotManager hideElements:(TopBarElementDownload | TopBarElementSnapshot)
forSlot:SlotTypeTopBar];
// 隐藏底部栏中的刷新按钮
[slotManager hideElements:BottomBarElementRefresh
forSlot:SlotTypeBottomBar];
// 禁用水平拖动手势(对 GestureControl 插槽,隐藏元素表示禁用对应手势)
[slotManager hideElements:GestureElementHorizontalDrag
forSlot:SlotTypeGestureControl];
// 显示之前隐藏的元素
[slotManager showElements:TopBarElementDownload
forSlot:SlotTypeTopBar];
// 批量设置所有插槽的隐藏元素(替换式)
[slotManager setAllHiddenElementMasks:@{
@(SlotTypeTopBar): @(TopBarElementDownload | TopBarElementSnapshot),
@(SlotTypeBottomBar): @(BottomBarElementRefresh),
}];
// 清空所有元素可见性配置
[slotManager clearAllElementMasks];元素位掩码类型:
插槽类型 | 元素类型 | 可控元素 |
TopBar |
| Back / Title / Download / Snapshot / Settings |
BottomBar |
| Play / Progress / Refresh / Speed |
SettingMenu |
| Speed / TrackInfo / Loop / Mute / MirrorMode / RotateMode / ScaleMode / Subtitle |
CenterDisplay |
| Volume / Brightness / Speed |
GestureControl |
| SingleTap / DoubleTap / LongPress / HorizontalDrag / LeftVerticalDrag / RightVerticalDrag |
PlayState |
| ErrorMessage / LoadingMessage |
各插槽子类通过 onRegisterElements 声明默认可见的元素组合。外部可通过 hideElements:forSlot: 覆盖默认可见的元素,或通过 showElements:forSlot: 覆盖默认隐藏的元素。最终可见性通过 isElementVisible: 查询。
场景可见性配置
通过 SlotConfig 控制插槽在不同场景下的可见性:
SlotManager *slotManager = playerView.slotManager;
// 配置封面图插槽在 Live 和 Restricted 场景下不可见
SlotConfig *coverConfig = [SlotConfig configWithSlotType:SlotTypeCover
excludedScenes:[NSSet setWithObjects:@(SceneTypeLive), @(SceneTypeRestricted), nil]];
[slotManager registerSlotConfig:coverConfig];
// 使用 visibilityCondition 进行动态可见性控制
SlotConfig *logConfig = [SlotConfig configWithSlotType:SlotTypeLogPanel
excludedScenes:[NSSet setWithObject:@(SceneTypeMinimal)]
visibilityCondition:^BOOL{
return [AliPlayerKit isDebugModeEnabled];
}];
[slotManager registerSlotConfig:logConfig];
// 移除场景可见性配置(该插槽在所有场景下均可见)
[slotManager unregisterSlotConfigForType:SlotTypeCover];
// 查询配置
SlotConfig *config = [slotManager slotConfigForType:SlotTypeCover];
BOOL visible = [config isVisibleInScene:SceneTypeLive];DefaultSlotFactory.defaultConfigs 已为所有内置插槽预置了默认的场景可见性配置。通过 registerSlotConfig: 可覆盖默认配置。
自定义开发
插槽协议(SlotProtocol)
所有插槽必须遵循 SlotProtocol,该协议定义了插槽的最小生命周期契约:
@protocol SlotProtocol <NSObject>
#pragma mark - View 生命周期
/// 插槽被附加到宿主时调用
- (void)onAttach:(id<SlotHost>)host;
/// 插槽从宿主分离时调用
- (void)onDetach;
#pragma mark - Data 生命周期
/// 数据绑定回调
- (void)onBindData:(AliPlayerModel *)model;
/// 数据解绑回调
- (void)onUnbindData;
@endSlotProvider 是构建插槽视图的 Block 类型:
typedef UIView<SlotProtocol> * _Nullable (^SlotProvider)(UIView *parentView);双生命周期
插槽采用独立的 View 生命周期和 Data 生命周期:
生命周期 | 回调方法 | 时机 | 职责 |
View 开始 |
|
| 建立宿主连接、订阅通知、初始化视图 |
Data 开始 |
|
| 绑定业务数据(封面图、标题等) |
Data 结束 |
| 播放数据被清除或更新前 | 清理数据相关资源 |
View 结束 |
|
| 释放所有资源,断开所有连接 |
┌─────────────────── 完整生命周期 ───────────────────┐
│ │
│ ① onAttach: ─── View 生命周期开始 │
│ ↓ 可访问 host、订阅通知、初始化子视图 │
│ │
│ ② onBindData: ─── Data 生命周期开始 │
│ ↓ 加载封面图、设置标题、配置场景行为 │
│ │
│ ③ onUnbindData ─── Data 生命周期结束 │
│ ↓ 清除图片缓存、重置 UI 状态 │
│ │
│ ④ onDetach ─── View 生命周期结束 │
│ ↓ 取消通知订阅、移除定时器、释放所有资源 │
│ │
└──────────────────────────────────────────────────────┘继承BaseSlot(推荐)
BaseSlot 是所有 UI 插槽的推荐基类,封装了生命周期管理、通知订阅、场景适配等通用逻辑。
核心属性:
属性 | 类型 | 说明 |
|
| 当前插槽所附属的宿主 |
|
| 当前插槽的类型标识符(由 |
|
| 需要避让的安全区域边缘(默认 |
|
| 延迟内容创建开关 |
|
| 内容是否已创建 |
|
| 触摸穿透开关(默认 |
核心方法:
方法 | 说明 |
| 子类重写以创建自定义 UI 元素 |
| 可见性控制 |
| 带动画的可见性控制 |
| 查询当前可见状态 |
| 通过宿主进行正确的生命周期管理来移除插槽 |
| 确保延迟内容已创建(幂等) |
| 安全读取当前场景类型 |
| 子类重写以声明支持的元素及其默认可见性 |
| 查询指定元素的最终可见性 |
安全区域避让 (SlotSafeAreaEdge)
typedef NS_OPTIONS(NSUInteger, SlotSafeAreaEdge) {
SlotSafeAreaEdgeNone = 0,
SlotSafeAreaEdgeTop = 1 << 0,
SlotSafeAreaEdgeLeft = 1 << 1,
SlotSafeAreaEdgeBottom = 1 << 2,
SlotSafeAreaEdgeRight = 1 << 3,
SlotSafeAreaEdgeAll = SlotSafeAreaEdgeTop | SlotSafeAreaEdgeLeft | SlotSafeAreaEdgeBottom | SlotSafeAreaEdgeRight,
};SlotHostLayout 在 layoutSubviews 中根据 safeAreaEdges 收缩对应边的 frame,使插槽内部布局直接使用自身锚点即可避开刘海、Home Indicator 等区域。子类可在 initWithFrame: 中设置此属性。
延迟内容创建 (deferContentCreation)
设为 YES 时,onAttach: 不会调用 setupContentView,内容将在首次调用 show 或 ensureContentReady 时创建。适用于使用频率较低的插槽(如 SettingMenu),避免在初始化时创建不必要的视图。
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.deferContentCreation = YES;
}
return self;
}自动隐藏延迟常量:
FOUNDATION_EXPORT const NSTimeInterval SlotAutoHideDelay;控制栏自动隐藏类插槽(如 TopBar、BottomBar)在无交互后的隐藏延迟时间(秒)。
自定义插槽完整示例
创建自定义插槽类,继承
BaseSlot:@interface MyCustomCoverSlot : BaseSlot @property (nonatomic, strong) UIImageView *coverImageView; @property (nonatomic, strong) UILabel *titleLabel; @end @implementation MyCustomCoverSlot - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 可选:配置安全区域避让 self.safeAreaEdges = SlotSafeAreaEdgeNone; } return self; } #pragma mark - 内容创建 - (void)setupContentView { _coverImageView = [[UIImageView alloc] init]; _coverImageView.contentMode = UIViewContentModeScaleAspectFill; _coverImageView.clipsToBounds = YES; [self addSubview:_coverImageView]; _titleLabel = [[UILabel alloc] init]; _titleLabel.textColor = [UIColor whiteColor]; _titleLabel.font = [UIFont boldSystemFontOfSize:16]; [self addSubview:_titleLabel]; } #pragma mark - 生命周期 - (void)onAttach:(id<SlotHost>)host { [super onAttach:host]; } - (void)onBindData:(AliPlayerModel *)model { [super onBindData:model]; if (model.coverUrl) { [_coverImageView sd_setImageWithURL:[NSURL URLWithString:model.coverUrl]]; } _titleLabel.text = model.videoTitle; [self show]; } - (void)onUnbindData { _coverImageView.image = nil; _titleLabel.text = nil; [super onUnbindData]; } - (void)onDetach { [super onDetach]; } #pragma mark - PlayerNotificationDelegate - (void)playerDidRenderFirstFrame { [self hideWithAnimation:YES]; } - (void)playerDidChangeState:(PlayerState)state { if (state == PlayerStateError) { [self show]; } } #pragma mark - 布局 - (void)layoutSubviews { [super layoutSubviews]; _coverImageView.frame = self.bounds; _titleLabel.frame = CGRectMake(16, self.bounds.size.height - 50, self.bounds.size.width - 32, 30); } @end说明BaseSlot遵循PlayerNotificationDelegate协议,在onAttach:时自动注册为控制器的通知代理。子类直接实现感兴趣的回调方法即可接收播放器事件,无需手动订阅。注册并使用:
SlotManager *slotManager = playerView.slotManager; [slotManager registerSlotType:SlotTypeCover provider:^UIView<SlotProtocol> *(UIView *parentView) { return [[MyCustomCoverSlot alloc] initWithFrame:parentView.bounds]; }]; [controller configure:model]; [playerView attach:controller];
元素级可见性声明
子类通过 onRegisterElements 声明支持的元素及其默认可见性:
@implementation MyCustomTopBarSlot
- (NSUInteger)onRegisterElements {
// 返回默认可见的元素位掩码
// Back 和 Title 默认可见,Download 和 Snapshot 默认不可见
return TopBarElementBack | TopBarElementTitle;
}
- (void)setupContentView {
// 根据元素可见性决定是否创建 UI
if ([self isElementVisible:TopBarElementBack]) {
// 创建返回按钮
}
if ([self isElementVisible:TopBarElementDownload]) {
// 创建下载按钮(默认不可见,但外部可通过 showElements 开启)
}
}
@endisElementVisible: 的合并逻辑如下:
外部调用
hideElements:forSlot:→ 强制隐藏。外部调用
showElements:forSlot:→ 强制显示。无外部覆盖时 → 使用
onRegisterElements声明的默认值。
自定义插槽类型(CustomSlotType)
除内置插槽类型外,可通过 CustomSlotType 在任意位置插入自定义插槽:
// 在 TopBar(80) 和 BottomBar(90) 之间插入自定义插槽
CustomSlotType *type = [CustomSlotType typeWithKey:@"myCustomSlot" order:85];
[playerView.slotManager registerCustomSlotType:type provider:^UIView<SlotProtocol> *(UIView *parentView) {
return [[MyCustomSlotView alloc] initWithFrame:parentView.bounds];
}];
// 检查自定义插槽是否已注册
BOOL registered = [playerView.slotManager isCustomSlotRegistered:type];
// 获取所有已注册的自定义插槽类型
NSArray<CustomSlotType *> *types = [playerView.slotManager customSlotTypes];
// 注销自定义插槽
[playerView.slotManager unregisterCustomSlotType:type];CustomSlotType 通过 key(唯一标识)和 order(排序值)定义。相等判断仅基于 key,同一 key 不可重复注册。自定义插槽在 rebuildSlots时按 order 与内置插槽统一排序。
在插槽中发送命令
插槽通过 host.controller 向控制器发送命令,实现单向数据流:
// 播放/暂停切换
- (void)onPlayPauseButtonTapped {
AliPlayerController *controller = self.host.controller;
if (controller.playerState == PlayerStatePlaying) {
[controller pause];
} else {
[controller play];
}
}
// 进度跳转
- (void)onSeekBarValueChanged:(UISlider *)slider {
long position = (long)(slider.value * self.host.playerStateStore.duration);
[self.host.controller seekTo:position];
}
// 全屏切换
- (void)onFullscreenButtonTapped {
BOOL isFullscreen = self.host.viewState.isFullscreen;
[self.host.controller setFullscreen:!isFullscreen];
}SlotBehavior(组合模式)
SlotBehavior 采用组合模式设计,使非 UIView 对象也能获得插槽能力(生命周期管理、事件订阅)。适用于纯逻辑组件需要监听播放器事件的场景。
@interface MyAnalyticsTracker : NSObject <PlayerNotificationDelegate>
@property (nonatomic, strong) SlotBehavior *behavior;
@end
@implementation MyAnalyticsTracker
- (instancetype)init {
self = [super init];
if (self) {
_behavior = [[SlotBehavior alloc] init];
}
return self;
}
- (void)attachToHost:(id<SlotHost>)host {
[self.behavior attachToHost:host];
// 通过 behavior 注册通知代理,detach 时自动取消订阅
[self.behavior addNotificationDelegate:self toController:host.controller];
}
- (void)detach {
[self.behavior detach];
}
#pragma mark - PlayerNotificationDelegate
- (void)playerDidRenderFirstFrame {
// 上报首帧渲染事件
}
- (void)playerDidEncounterError:(NSError *)error {
// 上报播放错误
}
@end核心优势:通过 addNotificationDelegate:toController: 注册的所有代理,在 detach 时自动取消注册,避免手动管理订阅生命周期导致的内存泄漏。
宿主协议(SlotHost)
SlotHost 协议由 SlotHostLayout(内部实现)遵循,为插槽提供上下文访问能力:
方法/属性 | 说明 |
| 返回宿主容器视图 |
| 获取当前播放器模型 |
| 获取播放器控制器(弱引用) |
| 获取播放器状态存储(只读) |
| 获取视图层级状态(全屏、控制栏可见性等) |
| 获取当前场景类型 |
| 为播放器设置渲染视图(由 |
| 解散插槽视图,确保生命周期管理的一致性 |
| 返回所有已挂载的插槽视图 |
| 根据类型获取已挂载插槽视图(O(1) 查找) |
| 获取插槽管理器(用于元素可见性查询) |
| 通知宿主:返回键被按下(非全屏状态) |
插槽应优先通过 host.sceneType 或 BaseSlot.currentSceneType 读取场景类型,而不是经由 host.model.sceneType 链式访问,避免在 model 尚未绑定时返回 nil 透传出的隐性 SceneTypeVOD(0) 默认值。
默认插槽工厂(DefaultSlotFactory)
DefaultSlotFactory 集中管理所有内置插槽的默认注册:
类方法 | 说明 |
| 返回 |
| 返回内置插槽的场景可见性配置数组( |
SlotManager 在初始化时一次性拉取这两份数据完成默认注册。新增或移除内置插槽仅需修改 DefaultSlotFactory。通过 SlotManager.clearAll 可清空所有配置并重新注册默认值。
技术原理
槽位选择策略
请求创建插槽 → 自定义注册表中有该类型?
├── 是 → 使用自定义实现
└── 否 → 默认注册表中有该类型?
├── 是 → 使用默认实现
└── 否 → 跳过(不创建)单向数据流
插槽系统采用单向数据流架构,插槽不直接持有控制器引用:
Controller → State / Event → Slot(状态下行,只读)
Slot → Command → Controller(命令上行,只发)状态下行:通过
host.playerStateStore主动查询,或通过PlayerNotificationDelegate回调被动接收。命令上行:通过
host.controller调用播放命令,由控制器执行。
插槽间隔离
插槽之间严禁直接获取对方实例或进行点对点通信。当一个插槽需要影响另一个插槽时:
发起插槽通过
host.controller发送状态变更。Controller 通过
PlayerNotificationDelegate通知所有关注此变化的插槽。
通过这种"事件上浮,状态下沉"的链路,避免了网状的 UI 耦合。
底部控制栏场景差异
控件 | VOD | LIVE | VIDEO_LIST | RESTRICTED | MINIMAL |
播放/暂停按钮 | ✓ | ✓ | ✓ | ✓ | ✗ |
进度条 | 可拖拽 | 不可拖拽 | 可拖拽 | 不可拖拽 | ✗ |
时间显示 | ✓ | ✓ | ✓ | ✓ | ✗ |
刷新按钮 | ✗ | ✓ | ✗ | ✗ | ✗ |
全屏按钮 | ✓ | ✓ | ✓ | ✓ | ✗ |
API速查
核心协议与类
类/协议 | 说明 |
| 插槽协议,定义最小生命周期契约 |
| 插槽基类,封装通用逻辑 |
| 插槽管理器,统一注册、可见性控制与运行时访问 |
| 宿主协议,提供上下文访问能力 |
| 宿主协议的内部实现( |
| 场景可见性配置 |
| 组合模式行为类,为非 UIView 对象提供插槽能力 |
| 默认插槽工厂,集中管理内置插槽注册 |
| 自定义插槽类型,支持在内置插槽之间插入 |
| 内置插槽类型枚举 |
SlotManager方法速查
分类 | 方法 | 说明 |
插槽注册 |
| 注册/覆盖内置插槽 Provider |
| 注销指定类型的插槽 | |
| 检查是否已注册 | |
自定义插槽 |
| 注册自定义插槽 |
| 注销自定义插槽 | |
| 检查自定义插槽是否已注册 | |
| 获取所有已注册的自定义插槽类型 | |
插槽可见性 |
| 整体隐藏/显示插槽 |
| 检查插槽是否被隐藏 | |
场景可见性 |
| 注册场景可见性配置 |
| 移除场景可见性配置 | |
| 获取场景可见性配置 | |
元素可见性 |
| 隐藏插槽内特定元素 |
| 显示插槽内特定元素 | |
| 替换隐藏元素掩码 | |
| 获取隐藏元素掩码 | |
| 获取显示元素掩码 | |
| 批量设置所有隐藏元素掩码 | |
| 清空所有元素可见性配置 | |
运行时 |
| 获取已挂载的插槽视图 |
| 重建所有插槽 | |
| 清空配置并重新注册默认值 |
示例参考
PlayerKitUsages/UsageSlotSystem 模块提供了完整的插槽系统使用示例,演示了默认插槽配置的开箱即用、通过 SlotManager 自定义插槽注册与隐藏,以及插槽生命周期的自动管理。