提交
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
//
|
||||
// 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user