Files
featherVoice/TUIKit/TUICallKit/TUICallKit-Swift/View/Root/GroupCallView.swift
2025-08-08 10:49:36 +08:00

284 lines
9.6 KiB
Swift

//
// GroupCallView.swift
// TUICallKit
//
// Created by vincepzhang on 2023/1/6.
//
class GroupCallView: UIView {
let selfCallStatusObserver = Observer()
let mediaTypeObserver = Observer()
private var isViewReady: Bool = false
let backgroundView = {
return GroupBackgroundView(frame: CGRect.zero)
}()
let maskedView = {
let maskedView = UIView(frame: CGRect.zero)
maskedView.backgroundColor = UIColor.t_colorWithHexString(color: "#22262E", alpha: 0.85)
return maskedView
}()
let layoutView = {
return GroupCallVideoLayout(frame: CGRect.zero)
}()
let floatingWindowBtn = {
return FloatingWindowButton(frame: CGRect.zero)
}()
let inviteUserButton = {
return InviteUserButton(frame: CGRect.zero)
}()
let inviterUserInfoView = {
return GroupCallerUserInfoView(frame: CGRect.zero)
}()
lazy var inviteeAvatarListView = {
return InviteeAvatarListView(frame: CGRect.zero)
}()
let waitingHintView = {
return CallWaitingHintView(frame: CGRect.zero)
}()
let functionView = {
let height = groupFunctionViewHeight + 30.scaleWidth()
return GroupCallerAndCalleeAcceptedView(frame: CGRect(x: 0, y: Screen_Height - height, width: Screen_Width, height: height))
}()
let inviteeWaitFunctionView = {
return AudioAndVideoCalleeWaitingView(frame: CGRect.zero)
}()
let timerView: TimerView = {
return TimerView(frame: CGRect.zero)
}()
let callStatusTipLabel: UILabel = {
let tipLabel = UILabel()
tipLabel.font = UIFont.systemFont(ofSize: 15.0)
tipLabel.textColor = UIColor.t_colorWithHexString(color: "#FFFFFF")
tipLabel.textAlignment = .center
tipLabel.text = TUICallKitLocalize(key: "TUICallKit.Group.waitAccept") ?? ""
return tipLabel
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.frame = CGRect(x: 0, y: 0, width: Screen_Width, height: Screen_Height)
backgroundColor = UIColor.t_colorWithHexString(color: "#303132")
functionView.delegate = self
createGroupCallView()
registerObserveState()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
TUICallState.instance.selfUser.value.callStatus.removeObserver(selfCallStatusObserver)
TUICallState.instance.mediaType.removeObserver(mediaTypeObserver)
for view in subviews {
view.removeFromSuperview()
}
}
// MARK: UI Specification Processing
override func didMoveToWindow() {
super.didMoveToWindow()
if isViewReady { return }
constructViewHierarchy()
activateConstraints()
isViewReady = true
}
func constructViewHierarchy() {
addSubview(backgroundView)
addSubview(maskedView)
addSubview(layoutView)
addSubview(floatingWindowBtn)
addSubview(inviteUserButton)
addSubview(inviterUserInfoView)
addSubview(waitingHintView)
addSubview(functionView)
addSubview(inviteeWaitFunctionView)
addSubview(timerView)
addSubview(callStatusTipLabel)
}
func activateConstraints() {
backgroundView.snp.makeConstraints { make in
make.edges.equalTo(self)
}
maskedView.snp.makeConstraints { make in
make.edges.equalTo(self)
}
layoutView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(StatusBar_Height + 48)
make.centerX.equalTo(self)
make.width.equalTo(Screen_Width)
make.bottom.equalTo(functionView.snp.top)
}
floatingWindowBtn.snp.makeConstraints { make in
make.top.equalToSuperview().offset(StatusBar_Height + 12.scaleHeight())
make.leading.equalToSuperview().offset(12.scaleWidth())
make.size.equalTo(kFloatWindowButtonSize)
}
inviteUserButton.snp.makeConstraints { make in
make.size.equalTo(kInviteUserButtonSize)
make.top.equalToSuperview().offset(StatusBar_Height + 12.scaleHeight())
make.trailing.equalTo(snp.trailing).offset(-12.scaleWidth())
}
inviterUserInfoView.snp.makeConstraints { make in
make.top.equalTo(self).offset(StatusBar_Height + 150.scaleHeight())
make.leading.equalTo(self).offset(20)
make.trailing.equalTo(self).offset(-20)
make.height.equalTo(100.scaleWidth() + 30.scaleHeight() + 50)
}
inviteeWaitFunctionView.snp.makeConstraints({ make in
make.centerX.equalTo(self)
make.bottom.equalTo(self.snp.bottom).offset(-Bottom_SafeHeight - 40.scaleHeight())
make.height.equalTo(60.scaleWidth() + 5.scaleHeight() + 20)
make.width.equalTo(self.snp.width)
})
timerView.snp.makeConstraints { make in
make.centerX.equalTo(self)
make.centerY.equalTo(floatingWindowBtn)
make.width.equalTo(200)
make.height.equalTo(30)
}
callStatusTipLabel.snp.makeConstraints { make in
make.edges.equalTo(timerView)
}
waitingHintView.snp.makeConstraints { make in
make.centerX.equalTo(self)
make.height.equalTo(20)
make.width.equalTo(self)
make.bottom.equalTo(self.timerView.snp.top)
}
}
func addInviteeAvatarListView() {
maskedView.addSubview(inviteeAvatarListView)
inviteeAvatarListView.snp.makeConstraints { make in
make.centerX.equalTo(maskedView)
make.width.equalTo(maskedView)
make.height.equalTo(60 + 5.scaleWidth())
make.top.equalTo(maskedView).offset(Screen_Height * 3 / 5)
}
}
func removeInviteeAvatarListView() {
inviteeAvatarListView.removeFromSuperview()
}
// MARK: View Create & Manage
func createGroupCallView() {
handleFloatingWindowBtn()
if TUICallState.instance.selfUser.value.callStatus.value == .waiting {
if TUICallState.instance.selfUser.value.callRole.value == .call {
createCallWaitingView()
} else if TUICallState.instance.selfUser.value.callRole.value == .called {
createCalledWaitingView()
}
} else if TUICallState.instance.selfUser.value.callStatus.value == .accept {
createCallingView()
}
}
func handleFloatingWindowBtn() {
if TUICallState.instance.enableFloatWindow {
floatingWindowBtn.isHidden = false
} else {
floatingWindowBtn.isHidden = true
}
}
func createCallWaitingView() {
hiddenChangeSubview()
layoutView.isHidden = false
inviteUserButton.isHidden = false
functionView.isHidden = false
callStatusTipLabel.isHidden = false
waitingHintView.isHidden = TUICallState.instance.selfUser.value.callRole.value == .call ? true : false
}
func createCalledWaitingView() {
hiddenChangeSubview()
inviterUserInfoView.isHidden = false
inviteeWaitFunctionView.isHidden = false
addInviteeAvatarListView()
}
func createCallingView() {
removeInviteeAvatarListView()
hiddenChangeSubview()
layoutView.isHidden = false
inviteUserButton.isHidden = false
functionView.isHidden = false
timerView.isHidden = false
}
func hiddenChangeSubview() {
layoutView.isHidden = true
inviteUserButton.isHidden = true
inviterUserInfoView.isHidden = true
functionView.isHidden = true
inviteeWaitFunctionView.isHidden = true
timerView.isHidden = true
callStatusTipLabel.isHidden = true
waitingHintView.isHidden = true
}
// MARK: Register TUICallState Observer && Update UI
func registerObserveState() {
callStatusChanged()
mediaTypeChanged()
}
func callStatusChanged() {
TUICallState.instance.selfUser.value.callStatus.addObserver(selfCallStatusObserver, closure: { [weak self] newValue, _ in
guard let self = self else { return }
self.createGroupCallView()
})
}
func mediaTypeChanged() {
TUICallState.instance.mediaType.addObserver(mediaTypeObserver) { [weak self] newValue, _ in
guard let self = self else { return }
self.createGroupCallView()
}
}
}
extension GroupCallView: GroupCallerAndCalleeAcceptedViewDelegate {
func showAnimation() {
UIView.animate(withDuration: groupFunctionAnimationDuration) {
self.functionView.frame = CGRect(x: 0,
y: Screen_Height - groupSmallFunctionViewHeight,
width: Screen_Width,
height: groupSmallFunctionViewHeight)
}
}
func restoreExpansion() {
UIView.animate(withDuration: groupFunctionAnimationDuration) {
self.functionView.frame = CGRect(x: 0, y: Screen_Height - groupFunctionViewHeight, width: Screen_Width, height: groupFunctionViewHeight)
}
}
func handleTransform(animationScale: CGFloat) {
let height = groupFunctionViewHeight - (groupFunctionViewHeight - groupSmallFunctionViewHeight) * animationScale
self.functionView.frame = CGRect(x: 0, y: Screen_Height - height, width: Screen_Width, height: height)
}
}