226 lines
7.9 KiB
Swift
226 lines
7.9 KiB
Swift
//
|
|
// RoomVideoFloatView.swift
|
|
// TUIRoomKit
|
|
//
|
|
// Created by janejntang on 2023/7/11.
|
|
//
|
|
|
|
import Foundation
|
|
import Factory
|
|
|
|
class RoomVideoFloatView: UIView {
|
|
@Injected(\.floatChatService) private var store: FloatChatStoreProvider
|
|
@Injected(\.conferenceStore) private var conferenceStore: ConferenceStore
|
|
private var isDraging: Bool = false
|
|
private let viewModel: RoomVideoFloatViewModel
|
|
private let space: CGFloat = 10
|
|
private let renderView: UIView = {
|
|
let view = UIView()
|
|
view.backgroundColor = UIColor(0x5C5C5C)
|
|
return view
|
|
}()
|
|
|
|
private let shutterView: UIView = {
|
|
let view = UIView()
|
|
view.backgroundColor = UIColor(0x17181F)
|
|
view.isHidden = true
|
|
return view
|
|
}()
|
|
|
|
private let avatarImageView: UIImageView = {
|
|
let imageView = UIImageView(frame: .zero)
|
|
imageView.layer.masksToBounds = true
|
|
imageView.isHidden = true
|
|
return imageView
|
|
}()
|
|
|
|
private let userStatusView: RoomUserStatusView = {
|
|
let view = RoomUserStatusView(frame: .zero)
|
|
return view
|
|
}()
|
|
|
|
override init(frame: CGRect) {
|
|
viewModel = RoomVideoFloatViewModel()
|
|
super.init(frame: frame)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
deinit {
|
|
NSObject.cancelPreviousPerformRequests(withTarget: self)
|
|
debugPrint("deinit:\(self)")
|
|
}
|
|
|
|
// MARK: - view layout
|
|
private var isViewReady: Bool = false
|
|
override func didMoveToWindow() {
|
|
super.didMoveToWindow()
|
|
guard !isViewReady else { return }
|
|
backgroundColor = .clear
|
|
constructViewHierarchy()
|
|
activateConstraints()
|
|
bindInteraction()
|
|
isViewReady = true
|
|
}
|
|
|
|
override func draw(_ rect: CGRect) {
|
|
super.draw(rect)
|
|
roundedRect(rect: bounds,
|
|
byRoundingCorners: .allCorners,
|
|
cornerRadii: CGSize(width: 10, height: 10))
|
|
avatarImageView.roundedCircle(rect: avatarImageView.bounds)
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
if !isDraging {
|
|
self.center = adsorption(centerPoint: self.center)
|
|
}
|
|
}
|
|
|
|
func constructViewHierarchy() {
|
|
addSubview(renderView)
|
|
addSubview(shutterView)
|
|
addSubview(avatarImageView)
|
|
addSubview(userStatusView)
|
|
}
|
|
|
|
func activateConstraints() {
|
|
renderView.snp.makeConstraints { make in
|
|
make.edges.equalToSuperview()
|
|
}
|
|
shutterView.snp.makeConstraints { make in
|
|
make.edges.equalToSuperview()
|
|
}
|
|
avatarImageView.snp.makeConstraints { make in
|
|
make.center.equalToSuperview()
|
|
make.height.width.equalTo(50)
|
|
}
|
|
userStatusView.snp.makeConstraints { make in
|
|
make.leading.equalToSuperview().offset(5)
|
|
make.bottom.equalToSuperview().offset(-5)
|
|
make.width.lessThanOrEqualTo(self).multipliedBy(0.9)
|
|
}
|
|
}
|
|
|
|
func bindInteraction() {
|
|
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didPan(panGesture:)))
|
|
addGestureRecognizer(panGesture)
|
|
let tap = UITapGestureRecognizer(target: self, action: #selector(didTap(sender:)))
|
|
addGestureRecognizer(tap)
|
|
viewModel.viewResponder = self
|
|
viewModel.showFloatWindowViewVideo(renderView: renderView)
|
|
setupViewState()
|
|
}
|
|
|
|
private func setupViewState() {
|
|
guard let userModel = viewModel.engineManager.store.attendeeList.first(where: { $0.userId == viewModel.userId }) else { return }
|
|
let placeholder = UIImage(named: "room_default_user", in: tuiRoomKitBundle(), compatibleWith: nil)
|
|
avatarImageView.sd_setImage(with: URL(string: userModel.avatarUrl), placeholderImage: placeholder)
|
|
userStatusView.updateUserVolume(hasAudio: userModel.hasAudioStream, volume: userModel.userVoiceVolume)
|
|
}
|
|
|
|
@objc func didTap(sender: UIView) {
|
|
viewModel.showRoomMainView()
|
|
}
|
|
|
|
@objc func didPan(panGesture: UIPanGestureRecognizer) {
|
|
guard let viewSuperview = superview else { return }
|
|
let moveState = panGesture.state
|
|
let viewCenter = center
|
|
switch moveState {
|
|
case .changed:
|
|
isDraging = true
|
|
let point = panGesture.translation(in: viewSuperview)
|
|
center = CGPoint(x: viewCenter.x + point.x, y: viewCenter.y + point.y)
|
|
break
|
|
case .ended:
|
|
let point = panGesture.translation(in: viewSuperview)
|
|
let newPoint = CGPoint(x: viewCenter.x + point.x, y: viewCenter.y + point.y)
|
|
UIView.animate(withDuration: 0.2) {
|
|
self.center = self.adsorption(centerPoint: newPoint)
|
|
}
|
|
isDraging = false
|
|
break
|
|
default: break
|
|
}
|
|
panGesture.setTranslation(.zero, in: viewSuperview)
|
|
}
|
|
|
|
class func show(width: CGFloat = 100, height: CGFloat = 180) {
|
|
DispatchQueue.main.async {
|
|
guard let currentWindow = RoomRouter.getCurrentWindow() else { return }
|
|
let roomFloatView = RoomVideoFloatView()
|
|
currentWindow.addSubview(roomFloatView)
|
|
roomFloatView.snp.makeConstraints { make in
|
|
make.trailing.equalToSuperview().offset(-5)
|
|
make.bottom.equalToSuperview().offset(-100)
|
|
make.width.equalTo(width)
|
|
make.height.equalTo(height)
|
|
}
|
|
}
|
|
}
|
|
|
|
class func dismiss() {
|
|
DispatchQueue.main.async {
|
|
guard let currentWindow = RoomRouter.getCurrentWindow() else { return }
|
|
for view in currentWindow.subviews where view is RoomVideoFloatView {
|
|
view.removeFromSuperview()
|
|
}
|
|
}
|
|
}
|
|
|
|
private func adsorption(centerPoint: CGPoint) -> CGPoint {
|
|
guard let viewSuperview = superview else { return centerPoint }
|
|
let limitMargin = 5.0
|
|
let frame = self.frame
|
|
let point = CGPoint(x: centerPoint.x - frame.width / 2, y: centerPoint.y - frame.height / 2)
|
|
var newPoint = point
|
|
if centerPoint.x < (viewSuperview.frame.width / 2) {
|
|
newPoint.x = limitMargin
|
|
} else {
|
|
newPoint.x = viewSuperview.frame.width - frame.width - limitMargin
|
|
}
|
|
if point.y <= limitMargin {
|
|
newPoint.y = limitMargin
|
|
} else if (point.y + frame.height) > (viewSuperview.frame.height - limitMargin) {
|
|
newPoint.y = viewSuperview.frame.height - frame.height - limitMargin
|
|
}
|
|
return CGPoint(x: newPoint.x + frame.width / 2, y: newPoint.y + frame.height / 2)
|
|
}
|
|
|
|
private func resetVolume() {
|
|
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(resetVolumeView), object: nil)
|
|
perform(#selector(resetVolumeView), with: nil, afterDelay: 1)
|
|
}
|
|
|
|
@objc func resetVolumeView() {
|
|
guard let userItem = viewModel.getUserEntity(userId: viewModel.userId) else { return }
|
|
userStatusView.updateUserVolume(hasAudio: userItem.hasAudioStream, volume: 0)
|
|
}
|
|
}
|
|
|
|
extension RoomVideoFloatView: RoomVideoFloatViewResponder {
|
|
func makeToast(text: String) {
|
|
RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
|
|
}
|
|
|
|
func updateUserStatus(user: UserEntity) {
|
|
let placeholder = UIImage(named: "room_default_user", in: tuiRoomKitBundle(), compatibleWith: nil)
|
|
avatarImageView.sd_setImage(with: URL(string: user.avatarUrl), placeholderImage: placeholder)
|
|
userStatusView.updateUserStatus(userModel: user)
|
|
}
|
|
|
|
func updateUserAudioVolume(hasAudio: Bool, volume: Int) {
|
|
userStatusView.updateUserVolume(hasAudio: hasAudio, volume: volume)
|
|
resetVolume()
|
|
}
|
|
|
|
func showAvatarImageView(isShow: Bool) {
|
|
shutterView.isHidden = !isShow
|
|
avatarImageView.isHidden = !isShow
|
|
}
|
|
}
|