Commit 8079abe0 authored by Alex朱枝文's avatar Alex朱枝文

增加直播点播入口

parent ce194313
...@@ -273,7 +273,11 @@ extension YHLifeViewController: UICollectionViewDelegate, UICollectionViewDataSo ...@@ -273,7 +273,11 @@ extension YHLifeViewController: UICollectionViewDelegate, UICollectionViewDataSo
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//跳转直播 文哥代办 //跳转直播 文哥代办
YHShareAlertView.show(image: "http://gips3.baidu.com/it/u=3886271102,3123389489&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960", title: "12344", subMessage: "12213243", linkUrl: "https://www.baidu.com", isLive: true) guard self.viewModel.liveArr.count > indexPath.row else {
return
}
let item = self.viewModel.liveArr[indexPath.row]
YHPlayerManager.shared.enterLive(from: nil, id: item.id, url: item.pull_url, title: item.live_title, roomId: item.room_id)
} }
} }
......
...@@ -24,8 +24,14 @@ class YHHomeBannerView: UIView { ...@@ -24,8 +24,14 @@ class YHHomeBannerView: UIView {
self.indicatorView.curIndicatorIndex = 0 self.indicatorView.curIndicatorIndex = 0
// 指定显示图片为第一个 // 指定显示图片为第一个
bannerView.selectItem(at: 0, animated: false) bannerView.selectItem(at: 0, animated: false)
let noNeedAutoScroll = dataArr.contains(where: {
$0.skip_type == 100 || $0.skip_type == 102
})
// // TODO: - alex测试
// noNeedAutoScroll = true
// // TODO: - alex测试
// 开启定时器开始滚动 // 开启定时器开始滚动
bannerView.automaticSlidingInterval = bannerSildingInterval bannerView.automaticSlidingInterval = noNeedAutoScroll ? 0 : bannerSildingInterval
bannerView.removesInfiniteLoopForSingleItem = true bannerView.removesInfiniteLoopForSingleItem = true
bannerView.alwaysBounceHorizontal = true bannerView.alwaysBounceHorizontal = true
} }
...@@ -132,6 +138,13 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate { ...@@ -132,6 +138,13 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
if model.isLocalItemFlag { if model.isLocalItemFlag {
return return
} }
// // TODO: - alex测试
// if index == 1 {
// let cell: YHHomeBannerCollectionViewCell? = pagerView.cellForItem(at: index) as? YHHomeBannerCollectionViewCell
// YHPlayerManager.shared.enterLive(from: cell?.bannerImagV, id: 23, url: "https://pull-flv-l6.douyincdn.com/stage/stream-116295918585905183.flv?k=e21f1ae1e7591521&t=1733551151&major_anchor_level=common&abr_pts=-800&_session_id=037-202411301359108030CAEAC1F742805E6D.1732946351732.18942&rsi=1", title: nil, roomId: nil, type: .secondary)
// return
// }
// // TODO: - alex测试
if model.skip_url.isEmpty == false { if model.skip_url.isEmpty == false {
switch model.skip_type { switch model.skip_type {
case 1: //跳转H5 case 1: //跳转H5
...@@ -201,6 +214,7 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate { ...@@ -201,6 +214,7 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
vc.url = model.skip_url vc.url = model.skip_url
self.parentViewController?.navigationController?.pushViewController(vc) self.parentViewController?.navigationController?.pushViewController(vc)
case 100:// 直播 case 100:// 直播
//文哥
// media_type 投放类型:1 图片,2 直播 // media_type 投放类型:1 图片,2 直播
// live_id 直播ID // live_id 直播ID
// live_status 直播状态 1:直播中 2:未直播 0:未知状态 3:结束直播 // live_status 直播状态 1:直播中 2:未直播 0:未知状态 3:结束直播
...@@ -208,11 +222,17 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate { ...@@ -208,11 +222,17 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
// live_pull_url 直播拉流链接 // live_pull_url 直播拉流链接
// video_url 视频链接 // video_url 视频链接
// recorded_cate_id 录播分类id // recorded_cate_id 录播分类id
let cell: YHHomeBannerCollectionViewCell? = pagerView.cellForItem(at: index) as? YHHomeBannerCollectionViewCell
YHPlayerManager.shared.enterLive(from: cell?.bannerImagV, id: model.live_id, url: model.live_pull_url, title: nil, roomId: nil, type: .secondary)
printLog("跳转直播") printLog("跳转直播")
case 101://录播 case 101://录播
printLog("跳转录播") printLog("跳转录播")
let cell: YHHomeBannerCollectionViewCell? = pagerView.cellForItem(at: index) as? YHHomeBannerCollectionViewCell
YHPlayerManager.shared.enterVOD(from: cell?.bannerImagV, id: model.live_id, url: model.video_url, title: nil, type: .secondary)
case 102://图片直播 case 102://图片直播
printLog("跳转录播") printLog("跳转录播")
let cell: YHHomeBannerCollectionViewCell? = pagerView.cellForItem(at: index) as? YHHomeBannerCollectionViewCell
YHPlayerManager.shared.enterLive(from: cell?.bannerImagV, id: model.live_id, url: model.live_pull_url, title: nil, roomId: nil, type: .secondary)
case 0://0 不需要跳转 case 0://0 不需要跳转
printLog("0 不需要跳转") printLog("0 不需要跳转")
default: default:
...@@ -233,4 +253,58 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate { ...@@ -233,4 +253,58 @@ extension YHHomeBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
func pagerViewDidEndScrollAnimation(_ pagerView: FSPagerView) { func pagerViewDidEndScrollAnimation(_ pagerView: FSPagerView) {
self.indicatorView.curIndicatorIndex = pagerView.currentIndex self.indicatorView.curIndicatorIndex = pagerView.currentIndex
} }
func pagerView(_ pagerView: FSPagerView, willDisplay cell: FSPagerViewCell, forItemAt index: Int) {
if index >= dataArr.count {
return
}
let model = dataArr[index]
if model.isLocalItemFlag {
return
}
// // TODO: - alex测试
// if let cell = cell as? YHHomeBannerCollectionViewCell {
// if index == 1 {
// YHPlayerManager.shared.play(url: "https://pull-flv-l11.douyincdn.com/thirdgame/stream-404525958790382412.flv?expire=1733554587&sign=d1e9f927e20f4a3fb4e2dd2a2712e256&major_anchor_level=common&abr_pts=-800&_session_id=037-20241130145626DBDEB00EB11CB388DD95.1732949787574.66743&rsi=1", inView: cell.bannerImagV, title: nil, type: .secondary)
// } else {
// let player = YHPlayerManager.shared.getPlayer(.secondary)
// player?.setPlayView(nil)
// }
// return
// }
// // TODO: - alex测试
if model.skip_url.isEmpty == false {
if let cell = cell as? YHHomeBannerCollectionViewCell {
if model.skip_type == 100 {
YHPlayerManager.shared.play(url: model.live_pull_url, inView: cell.bannerImagV, title: nil, type: .secondary)
} else {
let player = YHPlayerManager.shared.getPlayer(.secondary)
player?.setPlayView(nil)
}
}
}
}
func pagerView(_ pagerView: FSPagerView, didEndDisplaying cell: FSPagerViewCell, forItemAt index: Int) {
if index >= dataArr.count {
return
}
let model = dataArr[index]
if model.isLocalItemFlag {
return
}
// // TODO: - alex测试
// if index == 1 {
// YHPlayerManager.shared.stop(type: .secondary)
// return
// }
// // TODO: - alex测试
if model.skip_url.isEmpty == false {
if model.skip_type == 100 {
YHPlayerManager.shared.stop(type: .secondary)
}
}
}
} }
...@@ -90,7 +90,11 @@ extension YHSelectLookView: UICollectionViewDelegate, UICollectionViewDataSource ...@@ -90,7 +90,11 @@ extension YHSelectLookView: UICollectionViewDelegate, UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
//跳转直播 文哥代办 //跳转直播 文哥代办
guard items.count > indexPath.row else {
return
}
let item = items[indexPath.row]
YHPlayerManager.shared.enterLive(from: nil, id: item.id, url: item.pull_url, title: item.live_title, roomId: item.room_id)
} }
} }
......
...@@ -15,4 +15,16 @@ extension YHLivePlayerViewController { ...@@ -15,4 +15,16 @@ extension YHLivePlayerViewController {
callback(liveDetail, error) callback(liveDetail, error)
} }
} }
func joinLiveRoom(id: Int, callback: @escaping (_ success: Bool, _ error: YHErrorModel?) -> Void) {
viewModel.joinLiveRoom(id: id) { success, error in
callback(success, error)
}
}
func leaveLiveRoom(id: Int, callback: @escaping (_ success: Bool, _ error: YHErrorModel?) -> Void) {
viewModel.leaveLiveRoom(id: id) { success, error in
callback(success, error)
}
}
} }
...@@ -69,6 +69,9 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -69,6 +69,9 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
setupLiveUI() setupLiveUI()
setupLiveNotifications() setupLiveNotifications()
setupData() setupData()
if YHLoginManager.shared.isLogin() {
joinLiveRoom(id: liveId, callback: { _, _ in })
}
} }
deinit { deinit {
...@@ -95,6 +98,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -95,6 +98,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
topBarView.closeButtonClickEvent = { [weak self] in topBarView.closeButtonClickEvent = { [weak self] in
self?.quitChatRoom() self?.quitChatRoom()
self?.leaveLiveRoom()
YHPlayerManager.shared.stop(type: .main) YHPlayerManager.shared.stop(type: .main)
if let navigationController = self?.navigationController { if let navigationController = self?.navigationController {
navigationController.popViewController(animated: true) navigationController.popViewController(animated: true)
...@@ -105,6 +109,10 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -105,6 +109,10 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
topBarView.zoomButtonClickEvent = { [weak self] in topBarView.zoomButtonClickEvent = { [weak self] in
self?.enterFloating() self?.enterFloating()
} }
topBarView.shareButtonClickEvent = { [weak self] in
self?.shareLive()
}
} }
private func setupData() { private func setupData() {
...@@ -169,6 +177,9 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -169,6 +177,9 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
name: YHIMHelper.didLogOutEaseIM, name: YHIMHelper.didLogOutEaseIM,
object: nil object: nil
) )
NotificationCenter.default.addObserver(self, selector: #selector(didLoginYH), name: YhConstant.YhNotification.didLoginSuccessNotifiction, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didLogOutYH), name: YhConstant.YhNotification.didLogoutSuccessNotifiction, object: nil)
} }
// MARK: - Public Methods // MARK: - Public Methods
...@@ -207,6 +218,19 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -207,6 +218,19 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
} }
private func leaveLiveRoom() {
guard YHLoginManager.shared.isLogin() else {
return
}
leaveLiveRoom(id: liveId) { _, error in
if let error = error {
printLog("leaveLiveRoom: \(error)")
} else {
printLog("leaveLiveRoom: success")
}
}
}
private func quitChatRoom() { private func quitChatRoom() {
guard let roomId = roomId else { return } guard let roomId = roomId else { return }
...@@ -219,6 +243,13 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -219,6 +243,13 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
} }
private func shareLive() {
guard let liveModel = viewModel.liveDetailModel else {
return
}
YHShareAlertView.show(image: liveModel.live_image, title: "@" + liveModel.account, subMessage: liveModel.live_title, linkUrl: liveModel.live_h5_url, isLive: true)
}
// MARK: - Message Handling // MARK: - Message Handling
private func handleMessageInput(text: String, controller: YHMessageInputViewController) { private func handleMessageInput(text: String, controller: YHMessageInputViewController) {
guard checkLogin(), guard checkLogin(),
...@@ -276,6 +307,16 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -276,6 +307,16 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
appendHistoryMessages(messages) appendHistoryMessages(messages)
} }
@objc private func didLoginYH() {
if YHLoginManager.shared.isLogin() {
joinLiveRoom(id: liveId, callback: { _, _ in })
}
}
@objc private func didLogOutYH() {
//
}
@objc private func didLoginEaseIMSuccess() { @objc private func didLoginEaseIMSuccess() {
if let roomId = roomId { if let roomId = roomId {
joinChatRoom(roomId: roomId) joinChatRoom(roomId: roomId)
......
...@@ -16,10 +16,30 @@ class YHLiveDetailModel: SmartCodable { ...@@ -16,10 +16,30 @@ class YHLiveDetailModel: SmartCodable {
var hxUid: String = "" var hxUid: String = ""
var access_num: Int = 0 var access_num: Int = 0
var tips: String = "" var tips: String = ""
// 1:直播中 2:未直播 0:未知状态 3:结束直播
var status: Int = 0 var status: Int = 0
var roomId: String = "" var roomId: String = ""
var pullUrl: String = "" var pullUrl: String = ""
var goods: [YHLiveGoodsItem] = [] var goods: [YHLiveGoodsItem] = []
// 直播封面
var live_image: String = ""
// 直播主题
var live_title: String = ""
// h5直播地址
var live_h5_url: String = ""
// 预计开始时间
var start_time: String = ""
// 拉流地址360p
var pull_sd1_url: String = ""
// 拉流地址720p
var pull_hd_url: String = ""
// 1:直播中 2:未直播 0:未知状态 3:结束直播
var stream_status: Int = 0
// 实际开始时间
var actual_start_time: String = ""
// 实际结束时间
var actual_end_time: String = ""
required init() { required init() {
} }
......
...@@ -19,16 +19,16 @@ protocol YHFloatingWindowDelegate: AnyObject { ...@@ -19,16 +19,16 @@ protocol YHFloatingWindowDelegate: AnyObject {
class YHFloatingWindow: NSObject { class YHFloatingWindow: NSObject {
// MARK: - Properties // MARK: - Properties
weak var player: YHPlayer? weak var player: YHPlayer?
weak var delegate: YHFloatingWindowDelegate? weak var delegate: YHFloatingWindowDelegate?
var playbackInfo: YHPlayerManager.PlaybackInfo? var playbackInfo: YHPlayerManager.PlaybackInfo?
// 视频方向 // 视频方向
enum VideoOrientation { enum VideoOrientation {
case portrait // 竖屏 9:16 case portrait // 竖屏 9:16
case landscape // 横屏 16:9 case landscape // 横屏 16:9
var aspectRatio: CGFloat { var aspectRatio: CGFloat {
switch self { switch self {
case .portrait: return 9.0 / 16.0 case .portrait: return 9.0 / 16.0
...@@ -36,215 +36,219 @@ class YHFloatingWindow: NSObject { ...@@ -36,215 +36,219 @@ class YHFloatingWindow: NSObject {
} }
} }
} }
// 窗口尺寸 // 窗口尺寸
private struct Size { private struct Size {
static let minWidth: CGFloat = 120 static let minWidth: CGFloat = 120
static let maxWidth: CGFloat = UIScreen.main.bounds.width static let maxWidth: CGFloat = UIScreen.main.bounds.width
static let minHeight: CGFloat = 67.5 // 16:9 static let minHeight: CGFloat = 67.5 // 16:9
static let maxHeight: CGFloat = UIScreen.main.bounds.height static let maxHeight: CGFloat = UIScreen.main.bounds.height
static let defaultWidth: CGFloat = 150 static let defaultWidth: CGFloat = 150
static let defaultHeight: CGFloat = 84.375 // 16:9 static let defaultHeight: CGFloat = 84.375 // 16:9
} }
private(set) var contentView: UIView private(set) var contentView: UIView
private var containerView: UIView private var containerView: UIView
private var videoOrientation: VideoOrientation = .landscape private var videoOrientation: VideoOrientation = .landscape
// 手势相关 // 手势相关
private var initialFrame: CGRect = .zero private var initialFrame: CGRect = .zero
private var lastScale: CGFloat = 1.0
private var isResizing: Bool = false private var isResizing: Bool = false
private var initialCenter: CGPoint = .zero private var initialCenter: CGPoint = .zero
// 缩放相关
private var currentScale: CGFloat = 1.0
private var initialDistance: CGFloat = 0
private let scaleMultiplier: CGFloat = 1.5
// UI组件 // UI组件
private lazy var closeButton: UIButton = { private lazy var closeButton: UIButton = {
let button = UIButton(type: .custom) let button = UIButton(type: .custom)
button.setImage(UIImage(named: "icon_close"), for: .normal) button.setImage(UIImage(named: "live_win_close"), for: .normal)
button.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside) button.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
return button return button
}() }()
private lazy var closeButtonContainer: UIView = {
let container = UIView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
container.backgroundColor = .clear
container.addSubview(closeButton)
closeButton.center = CGPoint(x: container.bounds.width/2, y: container.bounds.height/2)
return container
}()
// MARK: - Initialization // MARK: - Initialization
override init() { override init() {
// 创建容器视图
containerView = UIView(frame: CGRect(x: 0, y: 0, containerView = UIView(frame: CGRect(x: 0, y: 0,
width: Size.defaultWidth, width: Size.defaultWidth,
height: Size.defaultHeight)) height: Size.defaultHeight))
// 创建内容视图
contentView = UIView(frame: containerView.bounds) contentView = UIView(frame: containerView.bounds)
super.init() super.init()
setupUI() setupUI()
setupGestures() setupGestures()
} }
// MARK: - Setup // MARK: - Setup
private func setupUI() { private func setupUI() {
// 容器视图设置
containerView.backgroundColor = .black containerView.backgroundColor = .black
containerView.layer.cornerRadius = 8 containerView.layer.cornerRadius = 3
containerView.clipsToBounds = true containerView.clipsToBounds = true
containerView.layer.masksToBounds = true containerView.layer.masksToBounds = true
// 添加阴影
containerView.layer.shadowColor = UIColor.black.cgColor containerView.layer.shadowColor = UIColor.black.cgColor
containerView.layer.shadowOffset = CGSize(width: 0, height: 2) containerView.layer.shadowOffset = CGSize(width: 0, height: 2)
containerView.layer.shadowRadius = 4 containerView.layer.shadowRadius = 4
containerView.layer.shadowOpacity = 0.3 containerView.layer.shadowOpacity = 0.3
// 添加内容视图
containerView.addSubview(contentView) containerView.addSubview(contentView)
contentView.snp.makeConstraints { make in contentView.translatesAutoresizingMaskIntoConstraints = false
make.edges.equalToSuperview() NSLayoutConstraint.activate([
} contentView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
// 添加关闭按钮 contentView.topAnchor.constraint(equalTo: containerView.topAnchor),
containerView.addSubview(closeButton) contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
closeButton.snp.makeConstraints { make in ])
make.top.right.equalToSuperview().inset(8)
make.size.equalTo(CGSize(width: 24, height: 24)) containerView.addSubview(closeButtonContainer)
} closeButtonContainer.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
closeButtonContainer.topAnchor.constraint(equalTo: containerView.topAnchor),
closeButtonContainer.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
closeButtonContainer.widthAnchor.constraint(equalToConstant: 30),
closeButtonContainer.heightAnchor.constraint(equalToConstant: 30)
])
} }
private func setupGestures() { private func setupGestures() {
// 平移手势
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:))) let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
// 缩放手势
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:))) let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
// 点击手势
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
tapGesture.delegate = self tapGesture.delegate = self
containerView.addGestureRecognizer(panGesture) containerView.addGestureRecognizer(panGesture)
containerView.addGestureRecognizer(pinchGesture) containerView.addGestureRecognizer(pinchGesture)
containerView.addGestureRecognizer(tapGesture) containerView.addGestureRecognizer(tapGesture)
} }
// MARK: - Public Methods // MARK: - Public Methods
func calculateInitialFrame() -> CGRect { func calculateInitialFrame() -> CGRect {
// 计算浮窗的初始位置和大小 let width: CGFloat = Size.defaultWidth
let width: CGFloat = 150 // 或其他合适的宽度 let height: CGFloat = width / videoOrientation.aspectRatio
let height: CGFloat = width * 9 / 16 // 保持16:9比例
let x = UIScreen.main.bounds.width - width - 16 let x = UIScreen.main.bounds.width - width - 16
let y = UIScreen.main.bounds.height - height - 100 // 距离底部适当距离 let y = UIScreen.main.bounds.height - height - 100
return CGRect(x: x, y: y, width: width, height: height) return CGRect(x: x, y: y, width: width, height: height)
} }
func show(in window: UIWindow) { func show(in window: UIWindow) {
containerView.frame = calculateInitialFrame() containerView.frame = calculateInitialFrame()
window.addSubview(containerView) window.addSubview(containerView)
} }
func show(in window: UIWindow, at point: CGPoint? = nil) { func show(in window: UIWindow, at point: CGPoint? = nil) {
// 设置初始位置
if let point = point { if let point = point {
containerView.center = point containerView.center = point
} else { } else {
// 默认位置:右下角
containerView.frame.origin = CGPoint( containerView.frame.origin = CGPoint(
x: window.bounds.width - containerView.bounds.width - 20, x: window.bounds.width - containerView.bounds.width - 20,
y: window.bounds.height - containerView.bounds.height - 100 y: window.bounds.height - containerView.bounds.height - 100
) )
} }
window.addSubview(containerView) window.addSubview(containerView)
// 显示动画
containerView.alpha = 0 containerView.alpha = 0
// containerView.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
UIView.animate(withDuration: 0.3) { UIView.animate(withDuration: 0.3) {
self.containerView.alpha = 1 self.containerView.alpha = 1
self.containerView.transform = .identity
} }
} }
func dismiss() { func dismiss() {
UIView.animate(withDuration: 0.3, animations: { UIView.animate(withDuration: 0.3, animations: {
self.containerView.alpha = 0 self.containerView.alpha = 0
//self.containerView.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
}) { _ in }) { _ in
self.containerView.removeFromSuperview() self.containerView.removeFromSuperview()
} }
} }
func setVideoSize(_ size: CGSize) { func setVideoSize(_ size: CGSize) {
// 更新视频方向
let orientation: VideoOrientation = size.width > size.height ? .landscape : .portrait let orientation: VideoOrientation = size.width > size.height ? .landscape : .portrait
if orientation != videoOrientation { if orientation != videoOrientation {
videoOrientation = orientation videoOrientation = orientation
updateLayoutForOrientation() updateLayoutForOrientation()
} }
} }
// MARK: - Private Methods // MARK: - Private Methods
private func updateLayoutForOrientation() { private func updateLayoutForOrientation() {
let currentWidth = containerView.bounds.width let currentWidth = containerView.bounds.width
let newHeight = currentWidth / videoOrientation.aspectRatio let newHeight = currentWidth / videoOrientation.aspectRatio
UIView.animate(withDuration: 0.3) { UIView.animate(withDuration: 0.3) {
var frame = self.containerView.frame var frame = self.containerView.frame
frame.size.height = newHeight frame.size.height = newHeight
self.containerView.frame = frame self.containerView.frame = frame
// 通知代理
self.delegate?.floatingWindow(self, didChangeSize: frame.size) self.delegate?.floatingWindow(self, didChangeSize: frame.size)
} }
} }
private func snapToNearestSize() { private func snapToNearestSize() {
let currentWidth = containerView.bounds.width let currentWidth = containerView.bounds.width
// 定义三种尺寸状态 let sizeSteps: [CGFloat] = [
let smallWidth = Size.minWidth Size.minWidth,
let mediumWidth = Size.maxWidth * 0.5 Size.maxWidth * 0.33,
let largeWidth = Size.maxWidth Size.maxWidth * 0.5,
Size.maxWidth * 0.75,
// 确定目标尺寸 Size.maxWidth
let targetWidth: CGFloat ]
if currentWidth < (smallWidth + mediumWidth) / 2 {
targetWidth = smallWidth var targetWidth = sizeSteps[0]
} else if currentWidth < (mediumWidth + largeWidth) / 2 { var minDifference = abs(currentWidth - targetWidth)
targetWidth = mediumWidth
} else { for size in sizeSteps {
targetWidth = largeWidth let difference = abs(currentWidth - size)
if difference < minDifference {
minDifference = difference
targetWidth = size
}
} }
// 计算对应高度
let targetHeight = targetWidth / videoOrientation.aspectRatio let targetHeight = targetWidth / videoOrientation.aspectRatio
let centerX = containerView.center.x
// 执行动画 let centerY = containerView.center.y
UIView.animate(withDuration: 0.3) {
UIView.animate(withDuration: 0.3,
delay: 0,
options: [.curveEaseOut],
animations: {
var frame = self.containerView.frame var frame = self.containerView.frame
frame.size = CGSize(width: targetWidth, height: targetHeight) frame.size = CGSize(width: targetWidth, height: targetHeight)
frame.origin.x = self.containerView.center.x - targetWidth / 2 frame.origin.x = centerX - targetWidth / 2
frame.origin.y = self.containerView.center.y - targetHeight / 2 frame.origin.y = centerY - targetHeight / 2
self.containerView.frame = frame self.containerView.frame = frame
// 通知代理
self.delegate?.floatingWindow(self, didChangeSize: frame.size) self.delegate?.floatingWindow(self, didChangeSize: frame.size)
} })
} }
// MARK: - Gesture Handlers // MARK: - Gesture Handlers
@objc private func handlePan(_ gesture: UIPanGestureRecognizer) { @objc private func handlePan(_ gesture: UIPanGestureRecognizer) {
guard !isResizing else { return } guard !isResizing else { return }
let translation = gesture.translation(in: containerView.superview) let translation = gesture.translation(in: containerView.superview)
switch gesture.state { switch gesture.state {
case .began: case .began:
initialCenter = containerView.center initialCenter = containerView.center
case .changed: case .changed:
var newCenter = CGPoint( var newCenter = CGPoint(
x: initialCenter.x + translation.x, x: initialCenter.x + translation.x,
...@@ -252,113 +256,140 @@ class YHFloatingWindow: NSObject { ...@@ -252,113 +256,140 @@ class YHFloatingWindow: NSObject {
) )
newCenter = adjustedPosition(for: newCenter) newCenter = adjustedPosition(for: newCenter)
containerView.center = newCenter containerView.center = newCenter
// 通知代理
delegate?.floatingWindow(self, didChangePosition: newCenter) delegate?.floatingWindow(self, didChangePosition: newCenter)
case .ended: case .ended:
let velocity = gesture.velocity(in: containerView.superview) let velocity = gesture.velocity(in: containerView.superview)
handlePanEndedWithVelocity(velocity) handlePanEndedWithVelocity(velocity)
default: default:
break break
} }
} }
@objc private func handlePinch(_ gesture: UIPinchGestureRecognizer) { @objc private func handlePinch(_ gesture: UIPinchGestureRecognizer) {
switch gesture.state { switch gesture.state {
case .began: case .began:
isResizing = true isResizing = true
initialFrame = containerView.frame initialFrame = containerView.frame
lastScale = 1.0 currentScale = 1.0
let touch1 = gesture.location(ofTouch: 0, in: containerView)
let touch2 = gesture.location(ofTouch: 1, in: containerView)
initialDistance = hypot(touch2.x - touch1.x, touch2.y - touch1.y)
case .changed: case .changed:
let scale = gesture.scale / lastScale let touch1 = gesture.location(ofTouch: 0, in: containerView)
let newWidth = initialFrame.width * scale let touch2 = gesture.location(ofTouch: 1, in: containerView)
let currentDistance = hypot(touch2.x - touch1.x, touch2.y - touch1.y)
let scale = (currentDistance / initialDistance) * scaleMultiplier
let scaleDelta = scale / currentScale
currentScale = scale
let newWidth = initialFrame.width * scaleDelta
let newHeight = newWidth / videoOrientation.aspectRatio let newHeight = newWidth / videoOrientation.aspectRatio
var newFrame = initialFrame var newFrame = initialFrame
newFrame.size = constrainSize(CGSize(width: newWidth, height: newHeight)) newFrame.size = constrainSize(CGSize(width: newWidth, height: newHeight))
newFrame.origin.x = containerView.center.x - newFrame.width / 2
newFrame.origin.y = containerView.center.y - newFrame.height / 2 let centerX = containerView.center.x
let centerY = containerView.center.y
newFrame.origin.x = centerX - newFrame.width / 2
newFrame.origin.y = centerY - newFrame.height / 2
CATransaction.begin()
CATransaction.setDisableActions(true)
containerView.frame = newFrame containerView.frame = newFrame
CATransaction.commit()
// 通知代理
delegate?.floatingWindow(self, didChangeSize: newFrame.size) delegate?.floatingWindow(self, didChangeSize: newFrame.size)
case .ended: case .ended, .cancelled:
isResizing = false isResizing = false
snapToNearestSize() snapToNearestSize()
default: default:
break break
} }
} }
@objc private func handleTap(_ gesture: UITapGestureRecognizer) { @objc private func handleTap(_ gesture: UITapGestureRecognizer) {
let location = gesture.location(in: containerView)
if closeButtonContainer.frame.contains(location) {
return
}
delegate?.floatingWindowDidTap(self) delegate?.floatingWindowDidTap(self)
} }
@objc private func closeButtonTapped() { @objc private func closeButtonTapped() {
delegate?.floatingWindowDidClose(self) delegate?.floatingWindowDidClose(self)
dismiss() dismiss()
} }
// MARK: - Helper Methods // MARK: - Helper Methods
private func constrainSize(_ size: CGSize) -> CGSize { private func constrainSize(_ size: CGSize) -> CGSize {
var width = size.width var width = size.width
var height = size.height var height = size.height
// 限制最小/最大尺寸 if width < Size.minWidth {
width = max(min(width, Size.maxWidth), Size.minWidth) width = Size.minWidth
} else if width > Size.maxWidth {
width = Size.maxWidth
}
height = width / videoOrientation.aspectRatio height = width / videoOrientation.aspectRatio
if height > Size.maxHeight {
height = Size.maxHeight
width = height * videoOrientation.aspectRatio
}
return CGSize(width: width, height: height) return CGSize(width: width, height: height)
} }
private func adjustedPosition(for center: CGPoint) -> CGPoint { private func adjustedPosition(for center: CGPoint) -> CGPoint {
guard let superview = containerView.superview else { return center } guard let superview = containerView.superview else { return center }
var adjustedCenter = center var adjustedCenter = center
let halfWidth = containerView.bounds.width / 2 let halfWidth = containerView.bounds.width / 2
let halfHeight = containerView.bounds.height / 2 let halfHeight = containerView.bounds.height / 2
// 限制不超出屏幕边界
adjustedCenter.x = min(max(halfWidth, adjustedCenter.x),
superview.bounds.width - halfWidth)
adjustedCenter.y = min(max(halfHeight, adjustedCenter.y),
superview.bounds.height - halfHeight)
// 计算实际可用区域 let safeAreaInsets: UIEdgeInsets
let topLimit = superview.safeAreaInsets.top + halfHeight if #available(iOS 11.0, *) {
let bottomLimit = superview.bounds.height - superview.safeAreaInsets.bottom - halfHeight safeAreaInsets = superview.safeAreaInsets
} else {
// 限制不超出屏幕边界和安全区域 safeAreaInsets = .zero
}
let topLimit = safeAreaInsets.top + halfHeight
let bottomLimit = superview.bounds.height - safeAreaInsets.bottom - halfHeight
adjustedCenter.x = min(max(halfWidth, adjustedCenter.x), adjustedCenter.x = min(max(halfWidth, adjustedCenter.x),
superview.bounds.width - halfWidth) superview.bounds.width - halfWidth)
adjustedCenter.y = min(max(topLimit, adjustedCenter.y), adjustedCenter.y = min(max(topLimit, adjustedCenter.y),
bottomLimit) bottomLimit)
return adjustedCenter return adjustedCenter
} }
private func handlePanEndedWithVelocity(_ velocity: CGPoint) { private func handlePanEndedWithVelocity(_ velocity: CGPoint) {
let magnitude = sqrt((velocity.x * velocity.x) + (velocity.y * velocity.y)) let magnitude = sqrt((velocity.x * velocity.x) + (velocity.y * velocity.y))
let slideMultiplier = magnitude / 200 let slideMultiplier = magnitude / 200
let slideFactor = 0.1 * slideMultiplier let slideFactor = 0.1 * slideMultiplier
var finalCenter = CGPoint( var finalCenter = CGPoint(
x: containerView.center.x + (velocity.x * slideFactor), x: containerView.center.x + (velocity.x * slideFactor),
y: containerView.center.y + (velocity.y * slideFactor) y: containerView.center.y + (velocity.y * slideFactor)
) )
finalCenter = adjustedPosition(for: finalCenter) finalCenter = adjustedPosition(for: finalCenter)
UIView.animate(withDuration: 0.3) { UIView.animate(withDuration: 0.3) {
self.containerView.center = finalCenter self.containerView.center = finalCenter
// 通知代理
self.delegate?.floatingWindow(self, didChangePosition: finalCenter) self.delegate?.floatingWindow(self, didChangePosition: finalCenter)
} }
} }
...@@ -368,14 +399,14 @@ class YHFloatingWindow: NSObject { ...@@ -368,14 +399,14 @@ class YHFloatingWindow: NSObject {
extension YHFloatingWindow: UIGestureRecognizerDelegate { extension YHFloatingWindow: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true return true
} }
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldReceive touch: UITouch) -> Bool { shouldReceive touch: UITouch) -> Bool {
// 如果点击的是按钮,不触发点击手势 let location = touch.location(in: containerView)
if touch.view is UIButton { if closeButtonContainer.frame.contains(location) {
return false return false
} }
return true return true
......
...@@ -133,4 +133,46 @@ extension YHLiveSalesViewModel { ...@@ -133,4 +133,46 @@ extension YHLiveSalesViewModel {
callback(nil, err) callback(nil, err)
} }
} }
/*
离开直播间
*/
func leaveLiveRoom(id: Int, callback: @escaping (_ success: Bool, _ error: YHErrorModel?) -> Void) {
let strUrl = YHBaseUrlManager.shared.curURL() + YHAllApiName.LiveSales.leaveLiveRoom
_ = YHNetRequest.getRequest(url: strUrl, params: ["live_id": id]) { json, _ in
// 1. json字符串 转 对象
printLog("model 是 ==> \(json)")
if json.code == 200 {
callback(true, nil)
} else {
let err = YHErrorModel(errorCode: Int32(json.code), errorMsg: json.msg.isEmpty ? "" : json.msg)
callback(false, err)
}
} failBlock: { err in
callback(false, err)
}
}
/*
进入直播间
*/
func joinLiveRoom(id: Int, callback: @escaping (_ success: Bool, _ error: YHErrorModel?) -> Void) {
let strUrl = YHBaseUrlManager.shared.curURL() + YHAllApiName.LiveSales.joinLiveRoom
_ = YHNetRequest.getRequest(url: strUrl, params: ["live_id": id]) { json, _ in
// 1. json字符串 转 对象
printLog("model 是 ==> \(json)")
if json.code == 200 {
callback(true, nil)
} else {
let err = YHErrorModel(errorCode: Int32(json.code), errorMsg: json.msg.isEmpty ? "" : json.msg)
callback(false, err)
}
} failBlock: { err in
callback(false, err)
}
}
} }
...@@ -676,5 +676,9 @@ class YHAllApiName { ...@@ -676,5 +676,9 @@ class YHAllApiName {
static let categoryList = "super-app/goods/category-list" static let categoryList = "super-app/goods/category-list"
static let goodsList = "super-app/goods/list" static let goodsList = "super-app/goods/list"
// 离开直播间
static let leaveLiveRoom = "super-app/live/app-live-exit"
// 进入直播间--需求登录
static let joinLiveRoom = "super-app/live/app-live-join"
} }
} }
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "live_win_close@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "live_win_close@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment