iOS

功能介绍

观众连麦功能‌是一种实时互动交流方式,通过观众连麦功能,主播可以与多达9个观众进行实时互动,无论是解答问题、分享经验还是进行娱乐互动,都能极大地提升观众的参与感满意度。这种直接的互动和交流为商业化运营提供了更加便捷和高效的渠道,同时也为观众提供了更加个性化和定制化的购物体验。观众连麦功能适用于多个场景,包括电商直播、娱乐直播、在线教学等。
单人连麦
多人连麦







使用说明

观众发起连麦申请

点击连麦请求按钮
选择连麦方式
发送连麦请求,等待主播同意
主播同意后,连麦成功













主播处理观众连麦请求

收到观众的连麦请求
点击连麦用户,打开连麦面板
点击同意后,连麦成功










功能定制

自定义主播端连麦管理面板视图

如您需要自定义主播端连麦管理面板视图 ,请参考以下路径更改。
// 文件位置:TUILiveKit/Source/View/LiveRoom/View/Anchor/LivingView

Panel // 主播连麦相关视图目录
└── AnchorLinkControlPanel.swift // 连麦管理面板:可接受观众连麦、拒绝观众连麦、挂断连麦

自定义观众端连麦申请面板视图

如您需要自定义观众端申请连麦面板视图 ,请参考以下路径更改。
Swift
// 文件位置:TUILiveKit/Source/View/LiveRoom/View/Audience/LivingView

Panel // 观众连麦相关的视图目录
├── LinkMicTypePanel.swift // 观众连麦弹出的选择语音连麦还是视频连麦的视图
└── VideoLinkSettingPanel.swift // 视频连麦时相关参数设置面板视图

关键代码

观众连麦

TUILiveKit 观众连麦功能 主要是通过 SeatService 实现,在 SeatService 中,您可通过 takeSeat 接口,实现观众连麦功能。以观众 B 申请和主播 A 连麦为例,具体交互时序可参考下图:




观众发送连麦请求

Swift
// 文件位置:TUILiveKit/iOS/TUILiveKit/Source/Service/SeatService.swift

func takeSeat(index: Int?, requestCallback:@escaping RequestClosure) -> AnyPublisher<TakeSeatResult, InternalError> {
return Future<TakeSeatResult, InternalError> { [weak self] promise in
guard let self = self else { return }
let request = roomEngine.takeSeat(index ?? kRandomSeatIndex , timeout: kTimeoutValue) { requestId, operateUserId in
// 主播同意连麦请求的回调
} onRejected: { requestId, operateUserId, message in
// 主播拒绝连麦请求的回调
} onCancelled: { requestId, operateUserId in
// 观众自己主动取消连麦请求的回调
} onTimeout: { requestId, operateUserId in
// 观众请求连麦请求超时回调
} onError: { requestId, operateUserId, err, message in
// 发送连麦请求失败回调
}
requestCallback(request)
}
.eraseToAnyPublisher()
}

主播端收到连麦请求

Swift
// 文件位置:TUILiveKit/iOS/TUILiveKit/Source/Service/EngineServiceCenter.swift

func onRequestReceived(request: TUIRequest) {
guard let store = self.store else { return }
switch request.requestAction {
case .takeSeat:
let seatApplication = SeatApplication(request: request)
store.dispatch(action: SeatActions.addSeatApplication(payload: seatApplication))
let actions: [ActionTemplate<User>] = [SeatActions.addSeatApplicationUser]
let param = generateActionTemplateParamTuple(param: request.userId, actions: actions)
store.dispatch(action: UserActions.fetchUserInfo(payload: param))
case .remoteUserOnSeat:
store.dispatch(action: SeatActions.updateReceivedSeatInvitation(payload: SeatInvitation(request: request)))
default:
break
}
}

观众取消连麦请求

Swift
// 文件位置:TUILiveKit/iOS/TUILiveKit/Source/Service/SeatService.swift

func cancelRequest(requestId: String) -> AnyPublisher<Void, InternalError> {
return Future { [weak self] promise in
guard let self = self else { return }
roomEngine.cancelRequest(requestId) {
promise(.success(()))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}.eraseToAnyPublisher()
}

主播端收到取消连麦请求

Swift
// 文件位置:TUILiveKit/iOS/TUILiveKit/Source/Service/EngineServiceCenter.swift

func onRequestCancelled(requestId: String, userId: String) {
guard let store = self.store else { return }
let isContainApplicationRequest = store.selectCurrent(SeatSelectors.getSeatApplications).contains { $0.id == requestId }
if isContainApplicationRequest {
store.dispatch(action: SeatActions.removeSeatApplication(payload: requestId))
}
if store.selectCurrent(SeatSelectors.getReceivedSeatInvitation).id == requestId {
store.dispatch(action: SeatActions.updateReceivedSeatInvitation(payload: SeatInvitation()))
store.dispatch(action: ViewActions.toastEvent(payload: ToastInfo(message: .inviteCancelText)))
}
}

主播处理连麦请求

Swift
// 文件位置:TUILiveKit/iOS/TUILiveKit/Source/Service/SeatService.swift

func responseRemoteRequest(isAgree: Bool, requestId: String) -> AnyPublisher <Void, InternalError> {
return Future { [weak self] promise in
guard let self = self else { return }
roomEngine.responseRemoteRequest(requestId, agree: isAgree) {
promise(.success(()))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}

观众连麦成功后,主播挂断观众的连麦

Swift
// 文件位置:TUILiveKit/iOS/TUILiveKit/Source/Service/SeatService.swift

func kickSeat(seat: SeatInfo) -> AnyPublisher<Void, InternalError> {
return Future<Void, InternalError> { [weak self] promise in
guard let self = self else { return }
roomEngine.kickUserOffSeatByAdmin(seat.index, userId: seat.userId) {
promise(.success(()))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}

观众连麦成功后,观众结束连麦

Swift
// 文件位置:TUILiveKit/iOS/TUILiveKit/Source/Service/SeatService.swift

func leaveSeat() -> AnyPublisher<Void, InternalError> {
return Future<Void, InternalError> { [weak self] promise in
guard let self = self else { return }
roomEngine.leaveSeat {
promise(.success(()))
} onError: { err, message in
let error = InternalError(error: err, message: message)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}