iOS

最近更新时间:2024-10-09 16:02:11

我的收藏

功能介绍

PK 功能是一种激烈而富有趣味的实时互动方式,专为主播和观众设计。它允许来自不同房间的主播进行实时对抗,增加了直播的竞争性和观赏性。TUILiveKit 的 PK 功能支持多达9位主播同时参与对战,为主播提供了一个展示才华、比拼魅力的平台,同时也为观众带来了紧张刺激的观看体验。无论是才艺比拼、知识问答还是游戏竞技,PK 功能都能为主播和观众创造更多的互动机会,激发观众的参与热情提升直播的趣味性和吸引力,从而为双方带来更多的惊喜和价值,推动直播内容的多样化发展。
双人 PK 中
双人 PK 结果
多人 PK 中
多人 PK 结果













使用说明

说明:
在您准备发起 PK 或接受 PK 前,请确保您处于连线状态中。

主播发起 PK

点击 PK 按钮
停止 PK 等待
断开 PK










主播接收 PK

主播收到 PK 邀请
主播接受 PK







功能定制

自定义 PK 等待倒计时样式

如您需要自定义 PK 等待倒计时样式 ,请参考以下路径更改:
// 文件位置:iOS/TUILiveKit/Source/View/LiveRoom/View/Anchor/LivingView/Battle

├── BattleCountDownBackgroundView.swift // PK 等待倒计时背景样式
└── BattleCountDownView.swift // PK 等待倒计时前景样式

自定义双人 PK 比分样式

如您需要自定义双人 PK 比分样式 ,请参考以下路径更改:
// 文件位置:iOS/TUILiveKit/Source/View/LiveRoom/View/Common/Battle/SingleBattleScoreView.swift

class SingleBattleScoreView: UIView {
...
func constructViewHierarchy() {
// 视图层级构建
}

func activateConstraints() {
// 视图layout布局
}

}

自定义多人 PK 比分样式

如您需要自定义多人 PK 比分样式 ,请参考以下路径更改:
// 文件位置:iOS/TUILiveKit/Source/View/LiveRoom/View/Common/Battle/BattleMemberInfoView.swift

class BattleMemberInfoView: UIView {
...
func constructViewHierarchy() {
// 视图层级构建
}

func activateConstraints() {
// 视图layout布局
}

}

自定义 PK 比分结果样式

如您需要自定义 PK 比分结果样式 ,请参考以下路径更改:
// 文件位置:iOS/TUILiveKit/Source/View/LiveRoom/View/Common/Battle/BattleInfoView.swift

class BattleInfoView: UIView {
...
func showBattleResult(store: LiveStore) {
// PK结果展示
}
}

关键代码

主播 PK

TUILiveKit 主播 PK 功能主要是基于 BattleService 实现的,您可通过 store.serviceCenter.battleService 获取到 PK 管理类对象,进而调用PK相关 API 函数,实现 PK 功能。以主播 A 和主播 B 的 PK 为例,具体交互时序可参考下图。



说明:
邀请多人参加 PK 时,若被邀请方中有人接受了 PK,则仅 PK 发起方和 PK 邀请接受方和对应房间内观众会收到 onBattleStarted 回调。

主播 A 发起 PK

主播 A 通过调用requestBattle发起PK,在参数 config中传入 PK 最大时长、是否需要邀请方回复同意/拒绝,在参数 userIdList中传入主播 B 的 userId,在参数 timeout 中传入 PK 邀请等待时长。
// 文件位置:iOS/TUILiveKit/Source/Service/BattleService.swift
func requestBattle(config: TUIBattleConfig, userIdList: [String], timeout: TimeInterval) -> AnyPublisher<(TUIBattleInfo, [String: TUIBattleCode]), InternalError> {
return Future<(TUIBattleInfo, [String: TUIBattleCode]), InternalError> { [weak self] promise in
guard let self = self else { return }
self.battleManager.requestBattle(config: config, userIdList: userIdList, timeout: timeout) { battleInfo, resultMap in
var battleResult: [String: TUIBattleCode] = [:]
resultMap.forEach { (key: String, value: NSNumber) in
battleResult[key] = TUIBattleCode(rawValue: value.intValue) ?? .unknown
}
promise(.success((battleInfo, battleResult)))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}
主播 A 可通过 onBattleRequestAccept 接收请求同意回调。

主播收到 PK 请求

主播 B 通过 onBattleRequestReceived 接收 PK 请求回调。
// 文件位置:iOS/TUILiveKit/Source/Service/EngineServiceCenter.swift
func onBattleRequestReceived(battleInfo: TUIBattleInfo, inviter: TUIBattleUser, invitee: TUIBattleUser) {
guard let store = self.store else { return}
store.dispatch(action: BattleActions.onBattleRequestReceived(payload:(battleInfo.battleId, inviter)))
}
主播 B 通过调用 acceptBattle 接受 PK 请求。
// 文件位置:iOS/TUILiveKit/Source/Service/BattleService.swift

func acceptBattle(battleId: String) -> AnyPublisher<Void, InternalError> {
return Future<Void, InternalError> { [weak self] promise in
guard let self = self else { return }
self.battleManager.acceptBattle(battleId: battleId) {
promise(.success(()))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}
主播 A,B 以及房间内观众可通过 onBattleStarted 接收 PK 开始回调。
// 文件位置:iOS/TUILiveKit/Source/Service/EngineServiceCenter.swift
func onBattleStarted(battleInfo: TUIBattleInfo) {
guard let store = self.store else { return }
handleBattleStarted(battleInfo: battleInfo)
}
private func handleBattleStarted(battleInfo: TUIBattleInfo) {
guard let store = self.store else { return }
battleInfo.config.duration = battleInfo.config.duration + Double(battleInfo.startTime) - Date().timeIntervalSince1970
store.dispatch(action: BattleActions.onBattleStarted(payload: battleInfo))
let selfUserId = store.selectCurrent(UserSelectors.getSelfInfo).userId
if battleInfo.inviter.userId == selfUserId || battleInfo.inviteeList.contains(where: { $0.userId == selfUserId }) {
store.dispatch(action: BattleActions.setIsOnBattle(payload: true))
}
}

主播退出 PK

以主播 B 退出 PK 为例,交互时序可参考下图。



说明:
多人 PK 时:
当有主播退出 PK,则其余 PK 中主播和对应房间内观众会收到 onUserExitBattle 回调。
当 PK 到达预设 PK 时间,则 PK 中主播和对应房间内观众会收到 onBattleEnded 回调。
当 PK 中主播人数为2,且有主播退出 PK时, PK中主播和对应房间内观众会收到 onBattleEnded回调。
主播 B 调用 exitBattle退出PK。
// 文件位置:iOS/TUILiveKit/Source/Service/BattleService.swift
func exitBattle(battleId: String) -> AnyPublisher<Void, InternalError> {
return Future<Void, InternalError> { [weak self] promise in
guard let self = self else { return }
self.battleManager.exitBattle(battleId: battleId) {
promise(.success(()))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}
主播 A, B 以及房间内观众收到 onBattleEnded 回调,收到 PK 结束通知。
// 文件位置:iOS/TUILiveKit/Source/Service/EngineServiceCenter.swift
func onBattleEnded(battleInfo: TUIBattleInfo, reason: TUIBattleStoppedReason) {
guard let store = store else { return }
store.dispatch(action: BattleActions.onBattleEnded(payload:(battleInfo, reason)))
store.dispatch(action: BattleActions.setIsOnBattle(payload: false))
}