清晰度功能开发

This commit is contained in:
zjx 2025-06-21 10:53:52 +08:00
parent b9cb5596db
commit 6a54c45a7c
19 changed files with 431 additions and 30 deletions

View File

@ -146,6 +146,14 @@
BF5E75D32DE5692D00DE9DFE /* JXBaseAnimatedTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E75C12DE5692D00DE9DFE /* JXBaseAnimatedTransition.swift */; };
BF5E75D52DE56B2000DE9DFE /* VPHomeCagetoryRecommandContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E75D42DE56B2000DE9DFE /* VPHomeCagetoryRecommandContentCell.swift */; };
BF5E75DB2DE5B8B700DE9DFE /* VPMarqueeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E75DA2DE5B8B700DE9DFE /* VPMarqueeView.swift */; };
BF692AB52E0644B500A5C2DA /* VPRevolutionSelectedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AB42E0644B500A5C2DA /* VPRevolutionSelectedView.swift */; };
BF692AB72E06450C00A5C2DA /* VPRevolutionSelectedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF692AB62E06450C00A5C2DA /* VPRevolutionSelectedCell.swift */; };
BFA21D982E01477200B3573D /* VPStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D972E01477200B3573D /* VPStoreViewController.swift */; };
BFA21D9A2E01497F00B3573D /* VPStoreVipBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D992E01497F00B3573D /* VPStoreVipBuyView.swift */; };
BFA21D9C2E01628F00B3573D /* VPStoreCoinsBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9B2E01628F00B3573D /* VPStoreCoinsBuyView.swift */; };
BFA21D9E2E01684E00B3573D /* VPStoreCoinsItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9D2E01684E00B3573D /* VPStoreCoinsItemView.swift */; };
BFA21DA02E01688D00B3573D /* VPStoreCoinsBigItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9F2E01688D00B3573D /* VPStoreCoinsBigItemView.swift */; };
BFA21DA22E01696700B3573D /* VPStoreCoinsSmallItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21DA12E01696700B3573D /* VPStoreCoinsSmallItemView.swift */; };
BFCCE10D2DF951F600EDE165 /* SceneDelegate+APNS.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE10C2DF951ED00EDE165 /* SceneDelegate+APNS.swift */; };
BFCCE1122DF9638B00EDE165 /* VPVipAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE1112DF9638B00EDE165 /* VPVipAlertView.swift */; };
BFCCE1142DFAAC0900EDE165 /* VPLanguageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE1132DFAAC0900EDE165 /* VPLanguageViewController.swift */; };
@ -162,12 +170,6 @@
BFCCE13D2DFFFAC500EDE165 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE13B2DFFFAC500EDE165 /* NotificationService.swift */; };
BFCCE1402E000ACE00EDE165 /* VPAnpsAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE13F2E000ACE00EDE165 /* VPAnpsAlertView.swift */; };
BFCCE1422E000F4800EDE165 /* VPHomePlayHistoricalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCCE1412E000F4800EDE165 /* VPHomePlayHistoricalView.swift */; };
BFA21D982E01477200B3573D /* VPStoreViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D972E01477200B3573D /* VPStoreViewController.swift */; };
BFA21D9A2E01497F00B3573D /* VPStoreVipBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D992E01497F00B3573D /* VPStoreVipBuyView.swift */; };
BFA21D9C2E01628F00B3573D /* VPStoreCoinsBuyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9B2E01628F00B3573D /* VPStoreCoinsBuyView.swift */; };
BFA21D9E2E01684E00B3573D /* VPStoreCoinsItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9D2E01684E00B3573D /* VPStoreCoinsItemView.swift */; };
BFA21DA02E01688D00B3573D /* VPStoreCoinsBigItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21D9F2E01688D00B3573D /* VPStoreCoinsBigItemView.swift */; };
BFA21DA22E01696700B3573D /* VPStoreCoinsSmallItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA21DA12E01696700B3573D /* VPStoreCoinsSmallItemView.swift */; };
BFF5AFA42DE6F15E0044227A /* VPMeVipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFA32DE6F15E0044227A /* VPMeVipCell.swift */; };
BFF5AFA62DE700420044227A /* VPMeVipPrivilegeItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFA52DE700420044227A /* VPMeVipPrivilegeItemView.swift */; };
BFF5AFA82DE704DC0044227A /* VPMeCoinCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF5AFA72DE704DC0044227A /* VPMeCoinCell.swift */; };
@ -418,6 +420,14 @@
BF5E75C92DE5692D00DE9DFE /* UIViewController+JXTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+JXTransition.swift"; sourceTree = "<group>"; };
BF5E75D42DE56B2000DE9DFE /* VPHomeCagetoryRecommandContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomeCagetoryRecommandContentCell.swift; sourceTree = "<group>"; };
BF5E75DA2DE5B8B700DE9DFE /* VPMarqueeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPMarqueeView.swift; sourceTree = "<group>"; };
BF692AB42E0644B500A5C2DA /* VPRevolutionSelectedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPRevolutionSelectedView.swift; sourceTree = "<group>"; };
BF692AB62E06450C00A5C2DA /* VPRevolutionSelectedCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPRevolutionSelectedCell.swift; sourceTree = "<group>"; };
BFA21D972E01477200B3573D /* VPStoreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreViewController.swift; sourceTree = "<group>"; };
BFA21D992E01497F00B3573D /* VPStoreVipBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreVipBuyView.swift; sourceTree = "<group>"; };
BFA21D9B2E01628F00B3573D /* VPStoreCoinsBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsBuyView.swift; sourceTree = "<group>"; };
BFA21D9D2E01684E00B3573D /* VPStoreCoinsItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsItemView.swift; sourceTree = "<group>"; };
BFA21D9F2E01688D00B3573D /* VPStoreCoinsBigItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsBigItemView.swift; sourceTree = "<group>"; };
BFA21DA12E01696700B3573D /* VPStoreCoinsSmallItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsSmallItemView.swift; sourceTree = "<group>"; };
BFCCE10C2DF951ED00EDE165 /* SceneDelegate+APNS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SceneDelegate+APNS.swift"; sourceTree = "<group>"; };
BFCCE1112DF9638B00EDE165 /* VPVipAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPVipAlertView.swift; sourceTree = "<group>"; };
BFCCE1132DFAAC0900EDE165 /* VPLanguageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPLanguageViewController.swift; sourceTree = "<group>"; };
@ -433,12 +443,6 @@
BFCCE13B2DFFFAC500EDE165 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
BFCCE13F2E000ACE00EDE165 /* VPAnpsAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPAnpsAlertView.swift; sourceTree = "<group>"; };
BFCCE1412E000F4800EDE165 /* VPHomePlayHistoricalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPHomePlayHistoricalView.swift; sourceTree = "<group>"; };
BFA21D972E01477200B3573D /* VPStoreViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreViewController.swift; sourceTree = "<group>"; };
BFA21D992E01497F00B3573D /* VPStoreVipBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreVipBuyView.swift; sourceTree = "<group>"; };
BFA21D9B2E01628F00B3573D /* VPStoreCoinsBuyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsBuyView.swift; sourceTree = "<group>"; };
BFA21D9D2E01684E00B3573D /* VPStoreCoinsItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsItemView.swift; sourceTree = "<group>"; };
BFA21D9F2E01688D00B3573D /* VPStoreCoinsBigItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsBigItemView.swift; sourceTree = "<group>"; };
BFA21DA12E01696700B3573D /* VPStoreCoinsSmallItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPStoreCoinsSmallItemView.swift; sourceTree = "<group>"; };
BFF5AFA32DE6F15E0044227A /* VPMeVipCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPMeVipCell.swift; sourceTree = "<group>"; };
BFF5AFA52DE700420044227A /* VPMeVipPrivilegeItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPMeVipPrivilegeItemView.swift; sourceTree = "<group>"; };
BFF5AFA72DE704DC0044227A /* VPMeCoinCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPMeCoinCell.swift; sourceTree = "<group>"; };
@ -1002,6 +1006,8 @@
BFF5B2742DF2C3750044227A /* VPDetailRecommandView.swift */,
BFF5B2762DF2CA4B0044227A /* VPDetailRecommandBannerCell.swift */,
BFF5B4AE2DF6B6630044227A /* VPMutualCollectionView.swift */,
BF692AB42E0644B500A5C2DA /* VPRevolutionSelectedView.swift */,
BF692AB62E06450C00A5C2DA /* VPRevolutionSelectedCell.swift */,
);
path = View;
sourceTree = "<group>";
@ -1480,10 +1486,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Veloria/Pods-Veloria-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Veloria/Pods-Veloria-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Veloria/Pods-Veloria-frameworks.sh\"\n";
@ -1511,6 +1521,7 @@
BF0FA7B32DE447FE00C9E5F2 /* VPMeCell.swift in Sources */,
BF0FA6DF2DDC5E4D00C9E5F2 /* String+VPAdd.swift in Sources */,
BF0FA7002DDC665300C9E5F2 /* VPShortModel.swift in Sources */,
BF692AB72E06450C00A5C2DA /* VPRevolutionSelectedCell.swift in Sources */,
1B056E7B2DDB37BA007EE38D /* VPGradientView.swift in Sources */,
BF0FA7262DDC8F7600C9E5F2 /* VPCollectionView.swift in Sources */,
BF5E75DB2DE5B8B700DE9DFE /* VPMarqueeView.swift in Sources */,
@ -1554,6 +1565,7 @@
BF0FA73B2DDED1C700C9E5F2 /* VPHomeListCell.swift in Sources */,
BF0FA79F2DE1A29A00C9E5F2 /* VPCollectListCell.swift in Sources */,
BF0FA7592DDF1C2800C9E5F2 /* VPPlayerProgressView.swift in Sources */,
BF692AB52E0644B500A5C2DA /* VPRevolutionSelectedView.swift in Sources */,
BFF5AFE02DEEC5AB0044227A /* VPPlayerVipBuyView.swift in Sources */,
BFF5AFCA2DE97B7A0044227A /* VPWalletViewController.swift in Sources */,
BFF5B26C2DF28FD50044227A /* VPStatAPI.swift in Sources */,

View File

@ -10,7 +10,7 @@ import UIKit
class VPVideoAPI: NSObject {
///
static func requestVideoDetail(shortPlayId: String, activityId: String? = nil, completer: ((_ model: VPVideoDetailModel?) -> Void)?) {
static func requestVideoDetail(shortPlayId: String, activityId: String? = nil, revolution: VPShortModel.VideoRevolution? = nil, completer: ((_ model: VPVideoDetailModel?) -> Void)?) {
var parameters: [String : Any] = [
"short_play_id" : shortPlayId,
"video_id" : "0"
@ -20,6 +20,10 @@ class VPVideoAPI: NSObject {
parameters["activity_id"] = activityId
}
if let revolution = revolution?.rawValue {
parameters["revolution"] = revolution
}
var param = VPNetworkParameters(path: "/getVideoDetails")
param.method = .get
param.parameters = parameters

View File

@ -9,6 +9,8 @@ import UIKit
class VPLoginContentView: HWPanModalContentView {
var loginFinishBlock: (() -> Void)?
private lazy var bgView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "bg_image_01"))
return imageView
@ -167,6 +169,7 @@ extension VPLoginContentView {
VPHUD.dismiss()
guard let self = self else { return }
if isFinish {
self.loginFinishBlock?()
self.dismiss(animated: true) {
}

View File

@ -162,6 +162,11 @@ extension VPDetailPlayerViewController {
self.viewModel.handleUnlock = { [weak self] in
self?.unlockVideo()
}
self.viewModel.updateDetailDataBlock = { [weak self] toIndexPath in
guard let self = self else { return }
self.requestDetailData(indexPath: self.viewModel.currentIndexPath)
}
}
}
@ -171,7 +176,7 @@ extension VPDetailPlayerViewController {
let view = VPEpisodeView()
view.dataArr = detailModel?.episodeList ?? []
view.shortModel = detailModel?.shortPlayInfo
view.currentIndex = self.currentIndexPath.row
view.currentIndex = self.viewModel.currentIndexPath.row
view.didSelectedIndex = { [weak self] (index) in
self?.scrollToItem(indexPath: IndexPath(row: index, section: 0), animated: false)
}
@ -224,7 +229,7 @@ extension VPDetailPlayerViewController {
view.videoId = videoInfo.short_play_video_id
view.buyFinishBlock = { [weak self] in
guard let self = self else { return }
self.requestDetailData(indexPath: self.currentIndexPath)
self.requestDetailData(indexPath: self.viewModel.currentIndexPath)
}
view.present(in: nil)
}
@ -343,7 +348,7 @@ extension VPDetailPlayerViewController {
VPHUD.show(containerView: self.view)
VPVideoAPI.requestVideoDetail(shortPlayId: shortPlayId, activityId: activityId) { [weak self] model in
VPVideoAPI.requestVideoDetail(shortPlayId: shortPlayId, activityId: activityId, revolution: self.viewModel.revolution) { [weak self] model in
VPHUD.dismiss()
guard let self = self else { return }
guard let model = model else { return }

View File

@ -52,8 +52,6 @@ class VPVideoPlayerViewController: VPViewController {
private(set) var dataArr: [Any] = []
private(set) var currentIndexPath = IndexPath(row: 0, section: 0)
///
var autoNextEpisode = true
@ -120,7 +118,7 @@ class VPVideoPlayerViewController: VPViewController {
self.viewModel.isPlaying = true
if getDataCount() - currentIndexPath.row <= 2 {
if getDataCount() - viewModel.currentIndexPath.row <= 2 {
self.loadMoreData()
}
@ -144,7 +142,7 @@ class VPVideoPlayerViewController: VPViewController {
func clearDataArr() {
self.dataArr.removeAll()
self.viewModel.currentPlayer = nil
self.currentIndexPath = .init(row: 0, section: 0)
viewModel.currentIndexPath = .init(row: 0, section: 0)
self.collectionView.contentOffset = .init(x: 0, y: 0)
self.collectionView.reloadData()
}
@ -176,7 +174,7 @@ class VPVideoPlayerViewController: VPViewController {
func reloadData(completion: (() -> Void)? = nil) {
CATransaction.setCompletionBlock { [weak self] in
guard let self = self else { return }
let cell = self.collectionView.cellForItem(at: self.currentIndexPath) as? VPVideoPlayerCell
let cell = self.collectionView.cellForItem(at: viewModel.currentIndexPath) as? VPVideoPlayerCell
self.viewModel.currentPlayer = cell
completion?()
@ -190,7 +188,7 @@ class VPVideoPlayerViewController: VPViewController {
CATransaction.setCompletionBlock { [weak self] in
guard let self = self else { return }
if !animated {
if self.currentIndexPath != indexPath, indexPath.row < self.getDataCount() {
if viewModel.currentIndexPath != indexPath, indexPath.row < self.getDataCount() {
self.skip(indexPath: indexPath)
} else {
self.play()
@ -315,8 +313,8 @@ extension VPVideoPlayerViewController: UICollectionViewDelegate, UICollectionVie
}
}
if self.viewModel.currentPlayer == nil, indexPath == currentIndexPath, let playerProtocol = cell as? VPPlayerProtocol {
self.currentIndexPath = indexPath
if self.viewModel.currentPlayer == nil, indexPath == viewModel.currentIndexPath, let playerProtocol = cell as? VPPlayerProtocol {
viewModel.currentIndexPath = indexPath
self.viewModel.currentPlayer = playerProtocol
didChangeIndexPathForVisible()
}
@ -349,7 +347,7 @@ extension VPVideoPlayerViewController: UICollectionViewDelegate, UICollectionVie
for indexPath in indexPaths {
guard let cell = self.collectionView.cellForItem(at: indexPath) else { continue }
if floor(offsetY) == floor(cell.frame.origin.y) {
if self.currentIndexPath != indexPath {
if viewModel.currentIndexPath != indexPath {
self.skip(indexPath: indexPath)
}
}
@ -357,7 +355,7 @@ extension VPVideoPlayerViewController: UICollectionViewDelegate, UICollectionVie
}
private func skip(indexPath: IndexPath) {
currentIndexPath = indexPath
viewModel.currentIndexPath = indexPath
guard let currentPlayer = self.collectionView.cellForItem(at: indexPath) as? VPPlayerProtocol else { return }
self.viewModel.currentPlayer = currentPlayer
// currentCell = self.collectionView.cellForItem(at: indexPath) as? BCListPlayerCell
@ -381,7 +379,7 @@ extension VPVideoPlayerViewController {
}
private func didChangeIndexPathForVisible() {
self.delegate?.vp_playerListViewController?(self, didChangeIndexPathForVisible: self.currentIndexPath)
self.delegate?.vp_playerListViewController?(self, didChangeIndexPathForVisible: viewModel.currentIndexPath)
}
}

View File

@ -10,6 +10,32 @@ import SmartCodable
class VPShortModel: VPModel, SmartCodable {
enum VideoRevolution: String, SmartCaseDefaultable {
case r_540 = "540"
case r_720 = "720"
case r_1080 = "1080"
var needLogin: Bool {
if self == .r_720 {
return true
} else {
return false
}
}
var needVip: Bool {
if self == .r_1080 {
return true
} else {
return false
}
}
var toString: String {
return "\(self.rawValue)P"
}
}
enum TagType: String, SmartCaseDefaultable {
case hot = "hot"
case new = "new"
@ -39,6 +65,8 @@ class VPShortModel: VPModel, SmartCodable {
var video_url: String?
var updated_at: String?
var revolution: VideoRevolution?
@IgnoredKey
var titleAttributedString: NSAttributedString?
@IgnoredKey

View File

@ -14,6 +14,7 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView {
self.viewModel?.addObserver(self, forKeyPath: "rateModel", options: .new, context: nil)
rateButton.setTitle(self.viewModel?.rateModel.formatString(), for: .normal)
revolutionButton.setNeedsUpdateConfiguration()
}
}
@ -30,6 +31,8 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView {
epView.setTitle("veloria_EP.".localizedReplace(text: videoInfo?.episode ?? "0"), for: .normal)
lockView.isHidden = !(videoInfo?.is_lock ?? false)
lockView.videoInfo = videoInfo
revolutionButton.setNeedsUpdateConfiguration()
}
}
@ -51,6 +54,15 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView {
}
}
override var isCurrent: Bool {
didSet {
if !isCurrent {
rateSelectedView.removeFromSuperview()
revolutionSelectedView.removeFromSuperview()
}
}
}
//MARK: -------------- UI --------------
private lazy var bottomView: VPGradientView = {
let view = VPGradientView()
@ -104,6 +116,23 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView {
return button
}()
///
private lazy var revolutionButton: UIButton = {
var config = UIButton.Configuration.plain()
config.contentInsets = .init(top: 0, leading: 12, bottom: 0, trailing: 12)
config.background.backgroundColor = .color949494(alpha: 0.4)
let button = UIButton(configuration: config)
button.layer.cornerRadius = 15
button.layer.masksToBounds = true
button.configurationUpdateHandler = { [weak self] button in
guard let self = self else { return }
button.configuration?.attributedTitle = AttributedString.createAttributedString(string: self.viewModel?.revolution.toString ?? "", color: .colorFFFFFF(), font: .fontRegular(ofSize: 13))
}
button.addTarget(self, action: #selector(handleRevolutionButton), for: .touchUpInside)
return button
}()
private lazy var timeLabel: UILabel = {
let label = UILabel()
label.font = .fontRegular(ofSize: 12)
@ -122,6 +151,16 @@ class VPDetailPlayerControlView: VPVideoPlayerControlView {
return view
}()
///
private lazy var revolutionSelectedView: VPRevolutionSelectedView = {
let view = VPRevolutionSelectedView()
view.didSelected = { [weak self] revolution in
guard let self = self else { return }
self.viewModel?.selectedRevolution(revolution: revolution)
}
return view
}()
private lazy var lockView: VPVideoLockView = {
let view = VPVideoLockView()
view.clickUnlockButton = { [weak self] in
@ -176,6 +215,15 @@ extension VPDetailPlayerControlView {
}
}
@objc private func handleRevolutionButton() {
addSubview(revolutionSelectedView)
revolutionSelectedView.currentRevolution = self.viewModel?.revolution
revolutionSelectedView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
extension VPDetailPlayerControlView {
@ -188,6 +236,7 @@ extension VPDetailPlayerControlView {
addSubview(epBgView)
epBgView.addSubview(epView)
epBgView.addSubview(allEpView)
addSubview(revolutionButton)
addSubview(rateButton)
addSubview(timeLabel)
addSubview(lockView)
@ -224,9 +273,14 @@ extension VPDetailPlayerControlView {
make.right.equalToSuperview().offset(-10)
}
revolutionButton.snp.makeConstraints { make in
make.height.top.equalTo(epBgView)
make.left.equalTo(epBgView.snp.right).offset(10)
}
rateButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-15)
make.left.equalTo(epBgView.snp.right).offset(10)
make.left.equalTo(revolutionButton.snp.right).offset(10)
make.height.top.equalTo(epBgView)
make.width.equalTo(50)
}

View File

@ -23,6 +23,7 @@ class VPPlayerRechargeView: HWPanModalContentView {
}
var buyFinishBlock: (() -> Void)?
var vipBuyFinishBlock: (() -> Void)?
//MARK: UI
private lazy var bgView: UIImageView = {
@ -64,7 +65,12 @@ class VPPlayerRechargeView: HWPanModalContentView {
private lazy var vipView: VPStoreVipBuyView = {
let view = VPStoreVipBuyView()
view.buyFinishBlock = { [weak self] in
self?.buyFinishBlock?()
if let vipBuyFinishBlock = self?.vipBuyFinishBlock {
self?.vipBuyFinishBlock?()
} else {
self?.buyFinishBlock?()
}
self?.dismiss(animated: true) {
}
}

View File

@ -0,0 +1,86 @@
//
// VPRevolutionSelectedCell.swift
// Veloria
//
// Created by on 2025/6/21.
//
import UIKit
class VPRevolutionSelectedCell: VPCollectionViewCell {
var videoRevolution: VPShortModel.VideoRevolution? {
didSet {
label.text = videoRevolution?.toString
if videoRevolution?.needLogin == true {
markImageView.isHidden = false
markImageView.image = UIImage(named: "mark_icon_01")
} else if videoRevolution?.needVip == true {
markImageView.isHidden = false
markImageView.image = UIImage(named: "mark_icon_02")
} else {
markImageView.isHidden = true
}
}
}
var vp_isSelected: Bool = false {
didSet {
if vp_isSelected {
contentView.vp_setGradientBorder()
contentView.backgroundColor = .color1C2D2F(alpha: 0.6)
} else {
contentView.vp_removeGradientBorder()
contentView.backgroundColor = .color000000(alpha: 0.3)
}
}
}
private lazy var label: UILabel = {
let label = UILabel()
label.font = .fontRegular(ofSize: 14)
label.textColor = .colorFFFFFF()
return label
}()
private lazy var markImageView: UIImageView = {
let imageView = UIImageView()
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
vp_setupUI()
}
@MainActor required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension VPRevolutionSelectedCell {
private func vp_setupUI() {
contentView.layer.cornerRadius = 6
contentView.layer.masksToBounds = false
contentView.addSubview(label)
contentView.addSubview(markImageView)
label.snp.makeConstraints { make in
make.center.equalToSuperview()
}
markImageView.snp.makeConstraints { make in
make.right.equalToSuperview()
make.top.equalToSuperview().offset(-5)
}
}
}

View File

@ -0,0 +1,107 @@
//
// VPRevolutionSelectedView.swift
// Veloria
//
// Created by on 2025/6/21.
//
import UIKit
class VPRevolutionSelectedView: UIView {
var currentRevolution: VPShortModel.VideoRevolution? {
didSet {
collectionView.reloadData()
}
}
var didSelected: ((_ revolution: VPShortModel.VideoRevolution) -> Void)?
private lazy var dataArr: [VPShortModel.VideoRevolution] = {
return [.r_540, .r_720, .r_1080]
}()
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.itemSize = .init(width: 100, height: 54)
layout.minimumLineSpacing = 10
layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15)
return layout
}()
private lazy var collectionView: VPCollectionView = {
let collectionView = VPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(VPRevolutionSelectedCell.self, forCellWithReuseIdentifier: "cell")
collectionView.showsHorizontalScrollIndicator = false
collectionView.layer.masksToBounds = false
return collectionView
}()
override init(frame: CGRect) {
super.init(frame: frame)
let tap = UITapGestureRecognizer(target: self, action: #selector(handleDismiss))
tap.delegate = self
self.addGestureRecognizer(tap)
vp_setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func handleDismiss() {
self.removeFromSuperview()
}
}
extension VPRevolutionSelectedView {
private func vp_setupUI() {
addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.left.right.equalToSuperview()
make.bottom.equalToSuperview().offset(-(UIScreen.tabbarSafeBottomMargin + 85))
make.height.equalTo(54)
}
}
}
//MARK: -------------- UICollectionViewDelegate UICollectionViewDataSource --------------
extension VPRevolutionSelectedView: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let videoRevolution = dataArr[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! VPRevolutionSelectedCell
cell.videoRevolution = videoRevolution
cell.vp_isSelected = currentRevolution == videoRevolution
return cell
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataArr.count
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let videoRevolution = dataArr[indexPath.row]
self.didSelected?(videoRevolution)
self.handleDismiss()
}
}
//MARK: -------------- UIGestureRecognizerDelegate --------------
extension VPRevolutionSelectedView: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view != self {
return false
} else {
return true
}
}
}

View File

@ -11,6 +11,7 @@ class VPVideoPlayViewModel: NSObject {
@objc dynamic var isPlaying: Bool = true
var currentIndexPath = IndexPath(row: 0, section: 0)
var currentPlayer: VPPlayerProtocol? {
didSet {
@ -29,6 +30,9 @@ class VPVideoPlayViewModel: NSObject {
}
}
///
lazy var revolution: VPShortModel.VideoRevolution = .r_540
///
func seekToTime(toTime: Int) {
self.currentPlayer?.seekToTime(toTime: toTime)
@ -44,4 +48,46 @@ class VPVideoPlayViewModel: NSObject {
var handleEpisode: (() -> Void)?
var handleUnlock: (() -> Void)?
///
var updateDetailDataBlock: ((_ toIndexPath: IndexPath?) -> Void)?
}
extension VPVideoPlayViewModel {
///
func selectedRevolution(revolution: VPShortModel.VideoRevolution) {
guard self.revolution != revolution else { return }
let userInfo = VPLoginManager.manager.userInfo
if revolution.needLogin, userInfo?.is_tourist != false {
VPLoginManager.manager.openLogin { [weak self] in
guard let self = self else { return }
self.revolution = revolution
self.updateDetailDataBlock?(self.currentIndexPath)
}
} else if revolution.needVip, userInfo?.is_vip != true {
guard let videoInfo = self.currentPlayer?.videoInfo else { return }
let view = VPPlayerRechargeView()
view.shortPlayId = videoInfo.short_play_id
view.videoId = videoInfo.short_play_video_id
view.buyFinishBlock = { [weak self] in
guard let self = self else { return }
self.updateDetailDataBlock?(self.currentIndexPath)
}
view.vipBuyFinishBlock = { [weak self] in
guard let self = self else { return }
self.revolution = revolution
self.updateDetailDataBlock?(self.currentIndexPath)
}
view.present(in: nil)
} else {
self.revolution = revolution
self.updateDetailDataBlock?(self.currentIndexPath)
}
}
}

View File

@ -8,6 +8,9 @@
import UIKit
class VPStoreViewController: VPViewController {
///vip
var vipBuyFinishBlock: (() -> Void)?
private lazy var scrollView: VPScrollView = {
let scrollView = VPScrollView()
@ -23,6 +26,10 @@ class VPStoreViewController: VPViewController {
private lazy var vipView: VPStoreVipBuyView = {
let view = VPStoreVipBuyView()
view.buyFinishBlock = { [weak self] in
guard let self = self else { return }
self.vipBuyFinishBlock?()
}
return view
}()

View File

@ -38,8 +38,9 @@ class VPLoginManager: NSObject {
UserDefaults.vp_setObject(token, forKey: kVPLoginTokenDefaultsKey)
}
func openLogin() {
func openLogin(finishHandle: (() -> Void)? = nil) {
let view = VPLoginContentView()
view.loginFinishBlock = finishHandle
view.present(in: nil)
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group 79@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group 79@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group 78@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group 78@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB