AudienceView 观众端核心 UI 组件。通过该组件,开发者可以快速搭建基础的观众直播界面。本文档将按照从基础按钮调整到复杂视图替换的顺序,指导开发者完成观众端界面的按需定制。
AudienceView 是一个滑动容器。AudienceView 上进行的,而是作用于它内部的单房间视图 AudienceLiveView。滑动容器在工作时会动态创建和展示这些单房间视图,开发者需要通过 AudienceViewDelegate 协议,在以下核心生命周期回调中获取到对应房间的视图实例(即回调参数中的 liveView),并在正确的时机对其进行定制与资源管理:方法 | 描述 |
audienceView(_:onCreateLiveView liveView:for:) | 视图创建回调。此回调会向外传递刚刚实例化的 AudienceLiveView。适用于提前确定的静态样式定制。由于容器会提前预加载下个房间,该回调触发时视图可能尚未显示在屏幕上。通常在这里通过 liveView 执行替换内置组件、配置固定按钮等一次性布局操作。 |
audienceView(_:liveViewDidAppear liveView:for:) | 视图展示回调。此回调会向外传递当前真正显示在屏幕上的 AudienceLiveView。适用于依赖实时状态或信令的动态调整。通常在这里记录当前活跃的 liveView 实例,以便在收到业务信令(如商品上架通知)时,精准定向更新当前屏幕上的 UI;或在此处开启房间专属的定时器与动效。 |
audienceView(_:liveViewDidDisappear liveView:for:) | 视图隐藏回调。当观众滑出该房间或关闭界面时触发。 适用于状态重置与资源清理。通常在这里清空活跃的 liveView 记录,销毁在该房间内弹出的自定义业务面板、停止动效或清理定时器。 |
AudienceLiveView 提供的核心自定义接口与属性如下:方法/属性 | 描述 |
topRightItems | 用于灵活配置直播间右上角的按钮集合,支持自由添加自定义按钮或调整内置按钮的布局。 |
bottomItems | 用于灵活配置直播间底部的按钮集合,支持自由添加自定义按钮或调整内置按钮的布局。 |
replace(node:with:) | 用于将指定位置的默认组件(如顶部信息区、底部操作栏),替换为开发者自定义的全新视图。 |
overlayView | 专属挂件图层,方便开发者自由添加需要在视频画面上方悬浮展示的全局业务 UI。 |
perform(action:) | 用于在自定义视图中直接触发内置默认逻辑,例如显示默认观众列表,显示默认连麦管理面板等。 |
import UIKitimport TUILiveKitimport SnapKitclass AudienceViewController: UIViewController {weak var currentLiveView: AudienceLiveView?override func viewDidLoad() {super.viewDidLoad()// 步骤 1:初始化 AudienceView 并添加到当前控制器let audienceView = AudienceView(roomId: "your_room_id")audienceView.delegate = selfview.addSubview(audienceView)audienceView.frame = view.bounds}// 业务方法:展示商品列表页func showProductListPanel() {print("展示商品列表页...")}// 业务方法:模拟收到 IM 商品推送通知func onReceiveProductPushMessage() {// 步骤 5:通过信令动态调整 UI 时,必须作用于当前正在展示的 liveViewguard let liveView = currentLiveView else { return }let productCard = AudienceProductCardView() // 自定义商品卡片视图let productTap = UITapGestureRecognizer(target: self, action: #selector(onProductCardTapped))productCard.addGestureRecognizer(productTap)liveView.overlayView.addSubview(productCard)productCard.snp.makeConstraints { make inmake.trailing.equalToSuperview().offset(-12)make.bottom.equalToSuperview().offset(-100)make.width.equalTo(150)}}}extension AudienceViewController: AudienceViewDelegate {// 步骤 2:在创建回调中,进行静态的 UI 样式配置func audienceView(_ audienceView: AudienceView, onCreateLiveView liveView: AudienceLiveView, for liveInfo: LiveInfo) {// 配置底部操作栏:隐藏连麦功能,并添加自定义“购物车”按钮let shopCartBtn = UIButton(type: .custom)shopCartBtn.setImage(UIImage(named: "shop_cart_icon"), for: .normal)shopCartBtn.addTarget(self, action: #selector(onShopCartTapped), for: .touchUpInside)// 重新排列底部栏:仅保留礼物、点赞,插入购物车按钮(不声明 .coGuest 即可隐藏连麦)liveView.bottomItems = [.gift, .like, .custom(shopCartBtn)]}// 步骤 3:在展示回调中,记录当前活跃视图,为后续动态信令更新做准备func audienceView(_ audienceView: AudienceView, liveViewDidAppear liveView: AudienceLiveView, for liveInfo: LiveInfo) {self.currentLiveView = liveView}// 步骤 4:在隐藏回调中,清理业务状态或销毁弹窗,防止视图复用导致状态错乱func audienceView(_ audienceView: AudienceView, liveViewDidDisappear liveView: AudienceLiveView, for liveInfo: LiveInfo) {if self.currentLiveView === liveView {self.currentLiveView = nil}}// 步骤 6:处理业务点击事件@objc func onShopCartTapped() { showProductListPanel() }@objc func onProductCardTapped() { showProductListPanel() }}class AudienceProductCardView: UIView {// 自定义的商品卡片}
bottomItems 属性灵活增删内置功能,或通过 .custom(UIView) 插入自定义按钮。
AudienceViewDelegate 回调,在相应的回调方法中把组装好的包含 AudienceBottomItem 枚举的数组重新赋值给视图属性。func audienceView(_ audienceView: AudienceView,onCreateLiveView liveView: AudienceLiveView,for liveInfo: LiveInfo) {// 步骤 1:准备自定义按钮视图let shopButton = UIButton(type: .custom)shopButton.setImage(UIImage(named: "shop_cart"), for: .normal)// 步骤 2:更新底部按钮数组,保留送礼,隐藏连麦,新增商品按钮liveView.bottomItems = [.gift,.custom(shopButton)]}
topRightItems 属性精简或增加控制按钮。
AudienceViewDelegate 回调,在相应的回调方法中将枚举项或自定义视图赋值给 topRightItems 属性。func audienceView(_ audienceView: AudienceView,onCreateLiveView liveView: AudienceLiveView,for liveInfo: LiveInfo) {// 步骤 1:准备自定义按钮视图let reportButton = UIButton(type: .custom)reportButton.setImage(UIImage(named: "report_btn"), for: .normal)// 步骤 2: 更新顶部按钮数组(保留人数和关闭,新增举报)liveView.topRightItems = [.audienceCount, .custom(reportButton), .close]}
replace 接口整体替换指定区域的视图。内部通过 AudienceNode 枚举定义了 5 个支持替换的区域,可以结合开头的“页面结构示意图”理解:AudienceNode | 说明 |
liveInfo | 左上角的主播与房间信息展示区域。 |
topRightButtons | 右上角的系统控制按钮区域。 |
networkInfo | 网络状态指示区域。 |
bottomRightBar | 右下角的业务操作栏区域。 |
barrageInput | 左下角的弹幕输入框触发区域。 |
replace 接口会将自定义视图放入指定的 slot 区域。视图的位置由框架控制,开发者无需设置。视图的尺寸由其自身决定,推荐使用以下两种方式之一声明尺寸:class MyInfoView: UIView {init() {super.init(frame: .zero)let label = UILabel()label.text = "直播中"addSubview(label)label.snp.makeConstraints { make inmake.edges.equalToSuperview().inset(12) //由子视图撑开视图}}}
class MyInfoView: UIView {override var intrinsicContentSize: CGSize {CGSize(width: 200, height: 44)}}
AudienceViewDelegate 回调,在相应的回调方法中调用 AudienceLiveView 组件的 replace 接口,传入需要替换的节点枚举和新建的自定义视图。func audienceView(_ audienceView: AudienceView,onCreateLiveView liveView: AudienceLiveView,for liveInfo: LiveInfo) {// 步骤 1:初始化符合布局规则的自定义视图let customInfoView = MyInfoView()customInfoView.backgroundColor = .darkGray// 步骤 2:调用替换接口更新特定节点liveView.replace(node: .liveInfo, with: customInfoView)}
perform(action:) 方法快速触发内置逻辑。支持的 AudienceAction 包括:展示礼物面板(.showGiftPanel)、展示观众列表(.showAudienceList)等。addTarget 或手势识别器为视图添加点击事件。perform 方法传入 AudienceAction 枚举,或执行其他业务代码。func audienceView(_ audienceView: AudienceView,onCreateLiveView liveView: AudienceLiveView,for liveInfo: LiveInfo) {let btn = MyGiftButton()btn.liveView = liveViewliveView.bottomItems = [.custom(btn)]}class MyGiftButton: UIButton {weak var liveView: AudienceLiveView? // 弱引用override init(frame: CGRect) {super.init(frame: frame)setImage(UIImage(named: "custom_gift"), for: .normal)addTarget(self, action: #selector(onTap), for: .touchUpInside)}required init?(coder: NSCoder) { fatalError() }@objc func onTap() {// 弹出默认礼物面板liveView?.perform(.showGiftPanel)// 或者触发自定义逻辑// presentCustomPanel()}}
perform 触发的内置默认面板无法满足具体的业务需求时,开发者可以彻底接管这部分逻辑,基于底层数据 Core SDK 构建全新的业务面板。AtomicXCore 获取房间、用户及状态数据,完全自主地完成自定义视图的搭建与交互绑定。在完成自定义控制器的构建后,建议使用内部封装的 AtomicPopover 组件将其弹出,以确保您的自定义面板也能获得与 SDK 内置面板一致的丝滑手势拦截与平滑动画。UIView,并在其内部引入 AtomicXCore 接口,用于拉取或监听底层业务数据以驱动 UI 更新。contentView 传入 AtomicPopover 容器,并通过系统方法进行展示。import AtomicXCore // 引入底层数据接口进行业务开发// 步骤1:构建自定义观众列表视图class CustomAudienceListView: UIView {override init(frame: CGRect) {super.init(frame: frame)self.backgroundColor = .whitesetupUI()bindLiveData()}required init?(coder: NSCoder) {fatalError("init(coder:) has not been implemented")}private func setupUI() {// 在此添加您的自定义 UI 控件,例如展示观众头像、用户等级等}private func bindLiveData() {// 使用 AtomicXCore 提供的核心接口获取当前房间状态或用户数据// 拿到核心数据后刷新上方构建的自定义 UI}}// 示例场景:从屏幕底部向上滑出一个高度占屏幕一半的完全自定义面板func presentBusinessPanel(from parentViewController: UIViewController) {// 步骤2:配置弹窗容器参数let config = AtomicPopover.AtomicPopoverConfig(position: .bottom,height: .ratio(0.5),animation: .slideFromBottom)// 步骤3:弹出自定义视图let audienceListView = CustomAudienceListView()let popover = AtomicPopover(contentView: audienceListView, configuration: config)parentViewController.present(popover, animated: true)}
AtomicXCore 接口实现自定义功能面板页onCreateLiveView 回调中将其添加至 overlayView。liveViewDidAppear 记录的当前活跃视图实例,再向其 overlayView 中动态添加控件。liveViewDidDisappear 回调中执行视图的 removeFromSuperview 操作。这能有效避免用户滑动切房时,因视图复用导致的 UI 状态异常。overlayView 中,并在点击回调中调用前文定义的自定义弹窗逻辑。import UIKitimport TUILiveKitclass LiveRoomController: UIViewController {private weak var currentLiveView: AudienceLiveView? // 在AudienceViewDelegate中获取当前的liveView// 示例场景:在画面左上角悬浮显示红包挂件,点击后联动弹出前文定义的业务面板func addRedPacketWidget() {// 步骤 1:创建挂件视图并开启交互let redPacketWidget = UIImageView(image: UIImage(named: "red_packet_icon"))redPacketWidget.frame = CGRect(x: 15, y: 120, width: 60, height: 60)redPacketWidget.isUserInteractionEnabled = true// 步骤 2:绑定点击事件let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleRedPacketClick))redPacketWidget.addGestureRecognizer(tapGesture)// 步骤 3:添加至覆盖图层currentLiveView?.overlayView.addSubview(redPacketWidget)}@objc func handleRedPacketClick() {// 在此处调用前文写好的 presentBusinessPanel 方法,弹出深度定制的业务视图// presentBusinessPanel(from: self)}}
.custom()传入自定义视图后,点击视图却无法触发绑定的事件。bottomRightBar),请务必确保内部子控件形成了完整的约束链以撑开父视图,或者为主视图重写了 intrinsicContentSize。如果父视图实际尺寸为 0,即使内部按钮在屏幕上可见,点击事件也会因超出父视图边界而被系统直接丢弃。isUserInteractionEnabled 属性。如果是 UIImageView 或普通的 UIView 容器,系统默认该属性为 false,必须手动将其设置为 true。Target):确认是否正确添加了 UITapGestureRecognizer 或调用了 addTarget 方法。另外,由于视图是在代理回调中动态生成的,请确保事件绑定的目标对象(如外部的 controller)在视图存活期间未被释放。AudienceLiveView 的 bottomItems 和 topRightItems 属性支持响应式更新。只需组装一个新的按钮数组并重新赋值,SDK 内部会自动触发视图刷新,无需手动重绘。liveViewDidAppear 回调中记录的活跃实例,切勿误修改处于“预加载”状态的相邻房间视图。intrinsicContentSize 属性。在滑动容器中,视图尺寸的自适应尤为重要,约束不完整极易导致视图在滑动切换时发生重叠或变形。replace(node:with:) 接口替换指定节点后,发现当开始滑动后当前屏幕上替换的自定义视图消失了,并且之后在滑动过程中一直不显示。这通常是因为开发者在不同的直播间视图中,错误地复用了同一个自定义视图实例。AudienceView 是一个滑动容器,为了保证滑动的流畅性,系统会提前预加载接下来的相邻房间。这意味着 onCreateLiveView 代理方法会被提前调用。在 iOS 中,一个 UIView 实例同一时刻只能有一个父视图。如果您传入了一个共享的全局视图实例,当预加载下一个房间时,该视图就会被系统从当前可视房间中强制移除,并添加到尚未展示的预加载房间中,从而导致当前屏幕上看不到该视图。// ❌ 错误做法:在外部持有一个共享的视图实例let sharedBrandView = MyBrandView()public func audienceView(_ audienceView: AudienceView, onCreateLiveView liveView: AudienceLiveView, for liveInfo: LiveInfo) {// ⚠️ 警告:当预加载下一个 liveView 时,sharedBrandView 会被从当前屏幕拔出,塞进下一个不可见的 liveView 中liveView.replace(node: .liveInfo, with: sharedBrandView)}
onCreateLiveView 回调中,每次都为新的 AudienceLiveView 创建一个全新的自定义视图实例。public func audienceView(_ audienceView: AudienceView, onCreateLiveView liveView: AudienceLiveView, for liveInfo: LiveInfo) {// ✅ 正确做法:每次触发回调时,都实例化一个全新的自定义视图let newBrandView = MyBrandView()liveView.replace(node: .liveInfo, with: newBrandView)}
Apakah halaman ini membantu?
Anda juga dapat Menghubungi Penjualan atau Mengirimkan Tiket untuk meminta bantuan.
masukan