tencent cloud

直播 SDK

动态与公告
TUILiveKit 产品动态
云直播推拉流 SDK 产品动态
新手指引
产品简介
产品概述
产品优势
性能数据
基本概念
购买指南
TRTC Live 价格总览
Live 视频直播计费说明
开通服务(TUILiveKit)
Demo 体验
Demo 体验指引
跑通 Demo(TUILiveKit)
接入指南
视频直播
准备工作
主播开播
观众观看
直播列表
语聊房
准备工作
主播开播
观众观看
直播列表
推流助手
推流助手(Electron 桌面应用)
推流助手(Web 桌面浏览器)
直播监播
监播页面(Web 桌面浏览器 React 版)
监播页面(Web 桌面浏览器 Vue 版)
UI 自定义
直播视频组件
视频源编辑组件
观众列表组件
聊天弹幕组件
媒体源配置面板
连麦管理面板
直播送礼组件
无 UI 集成
搭建视频直播
搭建语聊房
功能指南
关注主播(TUILiveKit)
至臻画质(TUILiveKit)
输入媒体流进房(TUILiveKit)
礼物系统(TUILiveKit)
客户端 API
Android
iOS
Web
服务端 API(TUILiveKit)
账号系统
REST API
第三方回调
错误码(TUILiveKit)
常见问题
平台编译
用户鉴权
云直播推拉流 SDK
产品简介
购买指南
Demo 体验
免费测试
SDK 下载
License 管理
高级功能
客户端 API
常见问题
无 UI 集成方案
API 文档
OSS information
OSS Attribution Notice

直播间列表(iOS)

PDF
聚焦模式
字号
最后更新时间: 2026-03-10 16:52:47
LiveListStoreAtomicXCore 中负责管理直播房间列表、创建、加入以及维护房间状态的核心模块。通过LiveListStore,可以为您的应用构建完整的直播生命周期管理。


核心功能

直播列表拉取:获取当前所有公开的直播间列表,支持分页加载。
直播生命周期管理:提供从创建、开播、加入、离开到结束直播的全套流程接口。
实时事件监听:监听直播结束、用户被踢出等关键事件。

核心概念

核心概念
类型
核心职责与描述
LiveInfo
struct
代表一个直播间的所有信息模型。包含了直播ID (liveID)、名称 (liveName)、房主信息 (liveOwner) 以及自定义元数据 (metaData) 等。
LiveListState
struct
代表直播列表模块的当前状态。其核心属性 liveList 是一个 [LiveInfo] 数组,存储了拉取到的直播列表;currentLive 则代表用户当前所在的直播间信息。
LiveListEvent
enum
代表直播房间的全局事件。分为 .onLiveEnded (直播结束) 和 .onKickedOutOfLive (被踢出直播) 两种,用于处理关键的房间状态变更。
LiveListStore
class
这是与直播列表和房间生命周期交互的核心管理类。它是一个全局单例 (shared),负责所有直播房间的创建、加入、信息更新等操作。

实现步骤

步骤1:组件集成

视频直播:请参考 快速接入 集成 AtomicXCore,并完成 主播开播观众观看 的功能实现
语聊房:请参考 快速接入 集成 AtomicXCore,并完成 主播开播观众观看 的功能实现

步骤2:实现观众从直播列表进入直播间

创建一个展示直播列表的页面,该页面使用 UICollectionView 来布局直播间卡片。当用户点击某个卡片时,获取该直播间的 liveId,并跳转到观众观看页面。
import AtomicXCore
import SnapKit
import RTCRoomEngine
import Combine

class LiveListViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {

private let liveListStore = LiveListStore.shared
private var cancellables = Set<AnyCancellable>()
private var liveList: [LiveInfo] = []
private var collectionView: UICollectionView!

override func viewDidLoad() {
super.viewDidLoad()
setupUI()
bindStore()
fetchLiveList()
}
private func bindStore() {
// 订阅 state,自动接收列表更新
liveListStore.state
.subscribe(StatePublisherSelector(keyPath: \\LiveListState.liveList))
.receive(on: DispatchQueue.main)
.sink { [weak self] fetchedList in
self?.liveList = fetchedList
self?.collectionView.reloadData()
}
.store(in: &cancellables)
}

private func fetchLiveList() {
liveListStore.fetchLiveList(cursor: "", count: 20) { result in
if case .failure(let error) = result {
print("直播列表拉取失败: \\(error.localizedDescription)")
}
}
}
// 当用户点击列表中的某个Cell时
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedLiveInfo = liveList[indexPath.item]
// 创建观众观看页面,并将 liveId 传入
let audienceVC = YourAudienceViewController(liveId: selectedLiveInfo.liveID)
audienceVC.modalPresentationStyle = .fullScreen
present(audienceVC, animated: true)
}
// --- UICollectionViewDataSource, Delegate, and UI setup methods ---
private func setupUI() {
let layout = UICollectionViewFlowLayout()
// ... (可以自定义您的布局)
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "LiveCell")
view.addSubview(collectionView)
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return liveList.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LiveCell", for: indexPath)
cell.backgroundColor = .lightGray // 自定义您的Cell样式
// let liveInfo = liveList[indexPath.item]
// ... (例如: cell.titleLabel.text = liveInfo.liveName)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// 单列、双列布局可通过自定义您的Cell大小来实现
let width = (view.bounds.width - 30) / 2
return CGSize(width: width, height: width * 1.2)
}
}
LiveInfo 参数说明
参数名
类型
描述
liveID
String
直播间的唯一标识符
liveName
String
直播间的标题
coverURL
String
直播间的封面图片地址
liveOwner
房主的个人信息
totalViewerCount
Int
直播间的总观看人数
categoryList
[NSNumber]
直播间的分类标签列表
notice
String
直播间的公告信息
metaData
[String: String]
开发者自定义的元数据,用于实现复杂的业务场景

功能进阶

场景一:实现直播列表的分类展示

在 App 的直播广场页,顶部设有“热门”、“音乐”、“游戏”等分类标签。用户点击不同的标签后,下方的直播列表会动态筛选,只展示对应分类的直播间,从而帮助用户快速发现感兴趣的内容。


实现方式

核心是利用 LiveInfo 模型中的 categoryList 属性。当主播开播设置分类后,fetchLiveList 返回的 LiveInfo 对象中就会包含这些分类信息。您的 App 在获取到完整的直播列表后,只需在客户端根据用户选择的分类,对这个列表进行一次简单的筛选,然后刷新 UI 即可。

代码示例

以下示例展示了如何在 LiveListViewController 中扩展一个 LiveListManager 来处理数据和筛选逻辑。
import AtomicXCore
import Combine

// 1. 创建一个数据管理器来封装数据获取和筛选逻辑
class LiveListManager {
private let liveListStore = LiveListStore.shared
private var cancellables = Set<AnyCancellable>()
private var fullLiveList: [LiveInfo] = []

// 对外暴露最终的直播列表
let filteredLiveListPublisher = CurrentValueSubject<[LiveInfo], Never>([])

init() {
liveListStore.state
.subscribe(StatePublisherSelector(keyPath: \\LiveListState.liveList))
.receive(on: DispatchQueue.main)
.sink { [weak self] fetchedList in
self?.fullLiveList = fetchedList
// 默认将完整列表发布出去
self?.filteredLiveListPublisher.send(fetchedList)
}
.store(in: &cancellables)
}

func fetchFirstPage() {
liveListStore.fetchLiveList(cursor: "", count: 20) { _ in }
}

/// 根据分类筛选直播列表
func filterLiveList(by categoryId: NSNumber?) {
guard let categoryId = categoryId else {
// 如果 categoryId 为 nil,则显示完整列表
filteredLiveListPublisher.send(fullLiveList)
return
}

let filteredList = fullLiveList.filter { liveInfo in
liveInfo.categoryList.contains(categoryId)
}
filteredLiveListPublisher.send(filteredList)
}
}

// 2. 在您的 LiveListViewController 中使用 Manager
class LiveListViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

private let manager = LiveListManager()
private var cancellables = Set<AnyCancellable>()
private var liveList: [LiveInfo] = []
private var collectionView: UICollectionView!

override func viewDidLoad() {
super.viewDidLoad()
// ... setupUI ...

// 绑定数据
manager.filteredLiveListPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] filteredList in
self?.liveList = filteredList
self?.collectionView.reloadData()
}
.store(in: &cancellables)

// 首次拉取
manager.fetchFirstPage()
}

// 当用户点击顶部分类标签 (UISegmentedControl) 时
@objc func categorySegmentDidChange(_ sender: UISegmentedControl) {
let selectedCategoryId: NSNumber? = 1 // 假设 "音乐" 分类的ID是 1
manager.filterLiveList(by: selectedCategoryId)
}

// ... (UICollectionView 相关代码)
}

场景二:实现直播列表的滑动播放

用户可以通过上下滑动来切换直播间,当一个新的直播间滑动到屏幕中央时,视频会自动开始播放预览;当它滑出屏幕时,视频则会自动停止,以节省带宽和设备性能。
说明:
滑动播放仅视频直播场景支持,语聊房场景正在规划中。

交互流程图



实现方式

LiveCoreView 支持多实例使用,我们为每一个 UICollectionViewCell 都创建一个独立的 LiveCoreView 实例。通过监听 UICollectionView 的滚动代理方法,我们可以精确地控制即将出现和已经离开屏幕的 Cell 中的 LiveCoreView 何时开始和停止拉流,从而实现“即滑即播、即走即停”的效果。

代码示例

我们创建一个自定义的 LiveFeedCell,它内部持有一个 LiveCoreView。然后在 UIViewController 中管理这些 Cell 的播放状态。
import UIKit
import AtomicXCore

// 1. 自定义 UICollectionViewCell,内部包含一个 LiveCoreView
class LiveFeedCell: UICollectionViewCell {
private var liveCoreView: LiveCoreView?

func setLiveInfo(_ liveInfo: LiveInfo) {
// 为新的直播信息创建一个新的 LiveCoreView
liveCoreView = LiveCoreView(viewType: .playView)
guard let liveCoreView = liveCoreView else { return }
contentView.addSubview(liveCoreView)
liveCoreView.frame = contentView.bounds
}

func startPlay(roomId: String) {
liveCoreView?.startPreviewLiveStream(roomId: roomId, isMuteAudio: false)
}

func stopPlay(roomId: String) {
liveCoreView?.stopPreviewLiveStream(roomId: roomId)
}
}

// 2. 在 ViewController 中管理播放逻辑
class LiveFeedViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

private var collectionView: UICollectionView!
private var liveList: [LiveInfo] = []
private var currentPlayingIndexPath: IndexPath?

// 当滑动完全停止后,此代理方法会被调用
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let page = Int(scrollView.contentOffset.y / view.frame.height)
let indexPath = IndexPath(item: page, section: 0)

// 只有当居中的 Cell 变化时才切换播放
if currentPlayingIndexPath != indexPath {
playVideo(at: indexPath)
}
}

// 当 Cell 即将离开屏幕时调用
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if let liveCell = cell as? LiveFeedCell {
let liveInfo = liveList[indexPath.item]
liveCell.stopPlay(roomId: liveInfo.liveID)
}
}

private func playVideo(at indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? LiveFeedCell {
let liveInfo = liveList[indexPath.item]
cell.startPlay(roomId: liveInfo.liveID)
currentPlayingIndexPath = indexPath
}
}

// ... (UICollectionViewDataSource 的其他方法)
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LiveFeedCell", for: indexPath) as! LiveFeedCell
cell.setLiveInfo(liveList[indexPath.item])
return cell
}
}

API 文档

关于 LiveListStore 及其相关类的所有公开接口、属性和方法的详细信息,请参阅随 AtomicXCore 框架的官方 API 文档。本文档使用到的相关 Store 如下:
Store/Component
功能描述
API 文档
LiveCoreView
直播视频流展示与交互的核心视图组件:负责视频流渲染和视图挂件处理,支持主播直播、观众连麦、主播连线等场景。
LiveListStore
直播间全生命周期管理:创建 / 加入 / 离开 / 销毁房间,查询房间列表,修改直播信息(名称、公告等),监听直播状态(例如被踢出、结束)。

常见问题

语聊房的列表和视频直播的列表数据是同一份吗?

它们的数据是同一份,您不需要分开拉取。LiveListStore 是一个全局单例,它负责统一管理应用中所有“直播”房间的生命周期,无论是视频直播还是语聊房。

直播列表中如何区分语聊房和视频直播

LiveListStore 本身不区分房间的业务类型。您需要在获取列表后,在客户端的应用层(业务逻辑或 UI 层)进行筛选和分类。
我们推荐采用以下两种方式进行区分:
方式一(推荐)使用 seatLayoutTemplateID 区分。这是一个用于定义房间布局的模板 ID,关于目前已支持的模板 ID 和效果,请参阅文档 开始直播 > 运行效果。您要在创建房间时指定 ID,然后在获取列表时根据 ID 范围来识别业务场景。
步骤1创建房间时指定 ID 调用 LiveListStore.shared.createLive 时,您需要根据业务场景为 LiveInfoseatLayoutTemplateID 属性传入指定范围的 ID
语聊房场景:传入 1 - 199 范围内的模板 ID
视频直播场景:传入 200 - 999 范围内的模板 ID
步骤2获取列表时筛选 客户端在 LiveListStore.state.liveList 中收到列表数据后,通过判断这个 ID 值所属的范围判断,从而在进入直播间的时候区分两种业务场景。
重要提示:
创建房间时,如果传入的 seatLayoutTemplateID 范围与您的业务场景(语聊房、视频直播)不匹配,可能会导致麦位布局出现功能异常。
方式二liveID 添加业务前缀。这是一种可选的、纯粹的应用层约定,用于辅助您在客户端快速筛选。
骤1:创建房间时添加前缀 在您生成 liveID 并调用 createLive 时,为不同业务类型的 liveID 赋予不同的前缀。例如:视频直播的 ID 以 “Live_” 开头 (例如:Live_12345),语聊房的 ID 以 “voice_” 开头 (例如:voice_67890)。
步骤2:获取列表时检查前缀 客户端在拉取到列表后,通过检查 liveID 字符串的前缀来进行区分。

帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈