From a2c6c0cc16bd880a26d17943afe3b73225382728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E8=A7=89=E6=96=B0?= Date: Thu, 17 Apr 2025 15:40:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=92=AD=E6=94=BE=E9=A1=B5=E9=9D=A2=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=BC=80=E5=8F=91=EF=BC=8C=E6=88=91=E7=9A=84=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Base/Controller/SPTabBarController.swift | 2 +- ShortPlay/Base/Extension/Int+SPAdd.swift | 30 ++++ ShortPlay/Base/Extension/UIColor+SPAdd.swift | 10 ++ ShortPlay/Base/View/SPScrollView.swift | 21 +++ ShortPlay/Base/View/SPTableView.swift | 2 +- .../Controller/SPExploreViewController.swift} | 14 +- .../Explore/View/SPExplorePlayerCell.swift | 16 ++ .../View/SPExplorePlayerControlView.swift | 63 ++++++++ .../Explore/View/SPVideoPlayerInfoView.swift | 112 +++++++++++++ .../Controller/SPMineViewController.swift | 78 ++++++++- ShortPlay/Class/Mine/Model/SPMineItem.swift | 32 ++++ ShortPlay/Class/Mine/View/SPMineCell.swift | 64 ++++++++ .../SPPlayerDetailViewController.swift | 43 +++++ .../Class/Player/Model/SPSpeedModel.swift | 50 +++++- .../Class/Player/View/SPEpisodeMenuView.swift | 149 +++++++++++++++++ .../Class/Player/View/SPEpisodeView.swift | 152 +++++++++++++++++- .../Player/View/SPPlayerControlView.swift | 33 ++++ .../View/SPPlayerDetailControlView.swift | 123 ++++++++++++++ .../Class/Player/View/SPPlayerListCell.swift | 5 + .../Player/View/SPPlayerProgressView.swift | 6 +- .../Player/View/SPSpeedSelectedCell.swift | 56 +++++++ .../Player/View/SPSpeedSelectedView.swift | 93 +++++++++++ .../ViewModel/SPPlayerListViewModel.swift | 9 +- .../about_us_icon_01.imageset/About Us@2x.png | Bin 0 -> 1738 bytes .../about_us_icon_01.imageset/About Us@3x.png | Bin 0 -> 3155 bytes .../about_us_icon_01.imageset/Contents.json | 22 +++ .../arrow_left_icon_01.imageset/Contents.json | 21 +++ .../nav_back@2x.png | Bin 0 -> 116 bytes .../Contents.json | 22 +++ .../Help Center@2x.png | Bin 0 -> 1625 bytes .../Help Center@3x.png | Bin 0 -> 3034 bytes .../language_icon_01.imageset/Contents.json | 22 +++ .../language_icon_01.imageset/Language@2x.png | Bin 0 -> 2877 bytes .../language_icon_01.imageset/Language@3x.png | Bin 0 -> 5377 bytes .../Contents.json | 22 +++ .../Order Record@2x.png | Bin 0 -> 1793 bytes .../Order Record@3x.png | Bin 0 -> 3357 bytes .../icon/play_icon_02.imageset/Contents.json | 22 +++ .../icon/play_icon_02.imageset/play@2x.png | Bin 0 -> 280 bytes .../icon/play_icon_02.imageset/play@3x.png | Bin 0 -> 431 bytes .../Contents.json | 22 +++ .../Privacy Policy@2x.png | Bin 0 -> 2027 bytes .../Privacy Policy@3x.png | Bin 0 -> 3851 bytes .../Contents.json | 22 +++ .../User Agreement@2x.png | Bin 0 -> 1397 bytes .../User Agreement@3x.png | Bin 0 -> 2313 bytes ShortPlay/Source/en.lproj/Localizable.strings | 10 ++ ShortPlay/Thirdparty/JXButton/JXButton.swift | 32 +++- 48 files changed, 1355 insertions(+), 25 deletions(-) create mode 100644 ShortPlay/Base/Extension/Int+SPAdd.swift create mode 100644 ShortPlay/Base/View/SPScrollView.swift rename ShortPlay/Class/{ForYou/Controller/SPForYouViewController.swift => Explore/Controller/SPExploreViewController.swift} (85%) create mode 100644 ShortPlay/Class/Explore/View/SPExplorePlayerCell.swift create mode 100644 ShortPlay/Class/Explore/View/SPExplorePlayerControlView.swift create mode 100644 ShortPlay/Class/Explore/View/SPVideoPlayerInfoView.swift create mode 100644 ShortPlay/Class/Mine/Model/SPMineItem.swift create mode 100644 ShortPlay/Class/Mine/View/SPMineCell.swift create mode 100644 ShortPlay/Class/Player/View/SPEpisodeMenuView.swift create mode 100644 ShortPlay/Class/Player/View/SPSpeedSelectedCell.swift create mode 100644 ShortPlay/Class/Player/View/SPSpeedSelectedView.swift create mode 100644 ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/About Us@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/About Us@3x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/Contents.json create mode 100644 ShortPlay/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Contents.json create mode 100644 ShortPlay/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/nav_back@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Contents.json create mode 100644 ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Help Center@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Help Center@3x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Contents.json create mode 100644 ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Language@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Language@3x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Contents.json create mode 100644 ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Order Record@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Order Record@3x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/Contents.json create mode 100644 ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/play@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/play@3x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Contents.json create mode 100644 ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Privacy Policy@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Privacy Policy@3x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/Contents.json create mode 100644 ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/User Agreement@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/User Agreement@3x.png diff --git a/ShortPlay/Base/Controller/SPTabBarController.swift b/ShortPlay/Base/Controller/SPTabBarController.swift index 77d08b9..b3eacf2 100644 --- a/ShortPlay/Base/Controller/SPTabBarController.swift +++ b/ShortPlay/Base/Controller/SPTabBarController.swift @@ -14,7 +14,7 @@ class SPTabBarController: UITabBarController { let nav1 = createNavigationController(viewController: SPHomePageController(), title: "Home".localized, image: UIImage(named: "tabbar_icon_01"), selectedImage: UIImage(named: "tabbar_icon_01_selected")) - let nav2 = createNavigationController(viewController: SPForYouViewController(), title: "For You".localized, image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected")) + let nav2 = createNavigationController(viewController: SPExploreViewController(), title: "For You".localized, image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected")) let nav5 = createNavigationController(viewController: SPMineViewController(), title: "Profile".localized, image: UIImage(named: "tabbar_icon_05"), selectedImage: UIImage(named: "tabbar_icon_05_selected")) diff --git a/ShortPlay/Base/Extension/Int+SPAdd.swift b/ShortPlay/Base/Extension/Int+SPAdd.swift new file mode 100644 index 0000000..81b1474 --- /dev/null +++ b/ShortPlay/Base/Extension/Int+SPAdd.swift @@ -0,0 +1,30 @@ +// +// Int+SPAdd.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/17. +// + +import UIKit + +class Int_SPAdd: NSObject { + +} + + +extension Int { + + func formatTimeGroup() -> (String, String, String) { + let seconds = self + + var s: String = "00" + var m: String = "00" + var h: String = "00" + s = String(format: "%02d", Int(Int(seconds) % 60)) + m = String(format: "%02d", Int(seconds / 60) % 60) + h = String(format: "%02d", Int(seconds / 3600)) + + return (h, m, s) + } + +} diff --git a/ShortPlay/Base/Extension/UIColor+SPAdd.swift b/ShortPlay/Base/Extension/UIColor+SPAdd.swift index 9af98a0..b6c0919 100644 --- a/ShortPlay/Base/Extension/UIColor+SPAdd.swift +++ b/ShortPlay/Base/Extension/UIColor+SPAdd.swift @@ -64,6 +64,16 @@ extension UIColor { return color(hex: 0xF56490, alpha: alpha) } + static func color9D9D9D(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0x9D9D9D, alpha: alpha) + } + static func color545454(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0x545454, alpha: alpha) + } + + static func colorD568D2(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0xD568D2, alpha: alpha) + } } diff --git a/ShortPlay/Base/View/SPScrollView.swift b/ShortPlay/Base/View/SPScrollView.swift new file mode 100644 index 0000000..89ef4ab --- /dev/null +++ b/ShortPlay/Base/View/SPScrollView.swift @@ -0,0 +1,21 @@ +// +// SPScrollView.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/17. +// + +import UIKit + +class SPScrollView: UIScrollView { + + override init(frame: CGRect) { + super.init(frame: frame) + self.contentInsetAdjustmentBehavior = .never + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/ShortPlay/Base/View/SPTableView.swift b/ShortPlay/Base/View/SPTableView.swift index 3dd43fe..a633c35 100644 --- a/ShortPlay/Base/View/SPTableView.swift +++ b/ShortPlay/Base/View/SPTableView.swift @@ -9,7 +9,7 @@ import UIKit class SPTableView: UITableView { - var insetGroupedMargins: CGFloat = 12 + var insetGroupedMargins: CGFloat = 15 override init(frame: CGRect, style: UITableView.Style) { super.init(frame: frame, style: style) diff --git a/ShortPlay/Class/ForYou/Controller/SPForYouViewController.swift b/ShortPlay/Class/Explore/Controller/SPExploreViewController.swift similarity index 85% rename from ShortPlay/Class/ForYou/Controller/SPForYouViewController.swift rename to ShortPlay/Class/Explore/Controller/SPExploreViewController.swift index f352ee2..bbbcb33 100644 --- a/ShortPlay/Class/ForYou/Controller/SPForYouViewController.swift +++ b/ShortPlay/Class/Explore/Controller/SPExploreViewController.swift @@ -1,5 +1,5 @@ // -// SPForYouViewController.swift +// SPExploreViewController.swift // ShortPlay // // Created by 曾觉新 on 2025/4/9. @@ -7,7 +7,11 @@ import UIKit -class SPForYouViewController: SPPlayerListViewController { +class SPExploreViewController: SPPlayerListViewController { + + override var PlayerCellClass: SPPlayerListCell.Type { + return SPExplorePlayerCell.self + } override func viewDidLoad() { super.viewDidLoad() @@ -27,7 +31,7 @@ class SPForYouViewController: SPPlayerListViewController { } //MARK: -------------- SPPlayerListViewControllerDelegate -------------- -extension SPForYouViewController: SPPlayerListViewControllerDelegate { +extension SPExploreViewController: SPPlayerListViewControllerDelegate { func sp_playerViewControllerLoadMoreData(playerViewController: SPPlayerListViewController) { guard let pagination = self.pagination else { return } guard let page = self.pagination?.current_page else { return } @@ -40,7 +44,7 @@ extension SPForYouViewController: SPPlayerListViewControllerDelegate { } //MARK: -------------- SPPlayerListViewControllerDataSource -------------- -extension SPForYouViewController: SPPlayerListViewControllerDataSource { +extension SPExploreViewController: SPPlayerListViewControllerDataSource { func sp_playerListViewController(_ viewController: SPPlayerListViewController, _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, oldCell: UICollectionViewCell) -> UICollectionViewCell { @@ -58,7 +62,7 @@ extension SPForYouViewController: SPPlayerListViewControllerDataSource { } } -extension SPForYouViewController { +extension SPExploreViewController { private func requestDataArr(page: Int) { diff --git a/ShortPlay/Class/Explore/View/SPExplorePlayerCell.swift b/ShortPlay/Class/Explore/View/SPExplorePlayerCell.swift new file mode 100644 index 0000000..00231c4 --- /dev/null +++ b/ShortPlay/Class/Explore/View/SPExplorePlayerCell.swift @@ -0,0 +1,16 @@ +// +// SPExplorePlayerCell.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/17. +// + +import UIKit + +class SPExplorePlayerCell: SPPlayerListCell { + + override var PlayerControlViewClass: SPPlayerControlView.Type { + return SPExplorePlayerControlView.self + } + +} diff --git a/ShortPlay/Class/Explore/View/SPExplorePlayerControlView.swift b/ShortPlay/Class/Explore/View/SPExplorePlayerControlView.swift new file mode 100644 index 0000000..eeea0d5 --- /dev/null +++ b/ShortPlay/Class/Explore/View/SPExplorePlayerControlView.swift @@ -0,0 +1,63 @@ +// +// SPExplorePlayerControlView.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/17. +// + +import UIKit + +class SPExplorePlayerControlView: SPPlayerControlView { + + + override var shortModel: SPShortModel? { + didSet { + desLabel.text = shortModel?.sp_description + videoInfoView.shortModel = shortModel + } + } + + private lazy var desLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 12) + label.textColor = .colorD2D2D2() + label.numberOfLines = 2 + return label + }() + + private lazy var videoInfoView: SPVideoPlayerInfoView = { + let view = SPVideoPlayerInfoView() + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.progressView.isHidden = true + + _setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SPExplorePlayerControlView { + + private func _setupUI() { + addSubview(desLabel) + addSubview(videoInfoView) + + desLabel.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.right.lessThanOrEqualToSuperview().offset(-30) + make.bottom.equalToSuperview().offset(-15) + } + + videoInfoView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.bottom.equalTo(desLabel.snp.top).offset(-10) + } + } +} diff --git a/ShortPlay/Class/Explore/View/SPVideoPlayerInfoView.swift b/ShortPlay/Class/Explore/View/SPVideoPlayerInfoView.swift new file mode 100644 index 0000000..840fcfc --- /dev/null +++ b/ShortPlay/Class/Explore/View/SPVideoPlayerInfoView.swift @@ -0,0 +1,112 @@ +// +// SPVideoPlayerInfoView.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/17. +// + +import UIKit + +class SPVideoPlayerInfoView: UIView { + + var shortModel: SPShortModel? { + didSet { + coverImageView.sp_setImage(url: shortModel?.image_url) + titleLabel.text = shortModel?.name + } + } + + //MARK: UI属性 + private lazy var bgView: UIView = { + let view = UIView() + view.layer.cornerRadius = 5 + view.layer.masksToBounds = true + view.backgroundColor = .color000000(alpha: 0.27) + return view + }() + + private lazy var coverImageView: SPImageView = { + let imageView = SPImageView() + imageView.layer.cornerRadius = 5 + imageView.layer.masksToBounds = true + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 13) + label.textColor = .colorFFFFFF() + return label + }() + + private lazy var moreButton: JXButton = { + let button = JXButton(type: .custom) + button.colors = [UIColor.colorF56490().cgColor, UIColor.colorD568D2().cgColor] + button.startPoint = .init(x: 0, y: 0.5) + button.endPoint = .init(x: 1, y: 0.5) + button.locations = [0, 1] + button.setImage(UIImage(named: "play_icon_02"), for: .normal) + button.setTitle("Series".localized, for: .normal) + button.setTitleColor(.colorFFFFFF(), for: .normal) + button.jx_font = .fontRegular(ofSize: 11) + button.layer.cornerRadius = 10.5 + button.layer.masksToBounds = true + button.space = 2 + button.addTarget(self, action: #selector(handleMoreButton), for: .touchUpInside) + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + _setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func handleMoreButton() { + let vc = SPPlayerDetailViewController() + vc.shortPlayId = shortModel?.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + + } +} + +extension SPVideoPlayerInfoView { + + private func _setupUI() { + addSubview(bgView) + addSubview(coverImageView) + addSubview(titleLabel) + addSubview(moreButton) + + bgView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.width.equalTo(240) + make.height.equalTo(54) + } + + coverImageView.snp.makeConstraints { make in + make.left.bottom.top.equalToSuperview() + make.width.equalTo(49) + make.height.equalTo(66) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalTo(coverImageView.snp.right).offset(10) + make.top.equalTo(bgView).offset(5) + make.right.lessThanOrEqualToSuperview().offset(-18) + } + + moreButton.snp.makeConstraints { make in + make.left.equalTo(coverImageView.snp.right).offset(10) + make.bottom.equalToSuperview().offset(-5) + make.width.equalTo(59) + make.height.equalTo(21) + } + + + } + +} diff --git a/ShortPlay/Class/Mine/Controller/SPMineViewController.swift b/ShortPlay/Class/Mine/Controller/SPMineViewController.swift index b897a5d..a4ad4f5 100644 --- a/ShortPlay/Class/Mine/Controller/SPMineViewController.swift +++ b/ShortPlay/Class/Mine/Controller/SPMineViewController.swift @@ -9,15 +9,87 @@ import UIKit class SPMineViewController: SPViewController { + private lazy var dataArr: [SPMineItem] = { + let arr = [ + SPMineItem(type: .orderRecord, iconImage: UIImage(named: "order_record_icon_01"), title: "Order Record".localized), + SPMineItem(type: .language, iconImage: UIImage(named: "language_icon_01"), title: "Language".localized), + SPMineItem(type: .privacyPolicy, iconImage: UIImage(named: "privacy_policy_icon_01"), title: "Privacy Policy".localized), + SPMineItem(type: .userAgreement, iconImage: UIImage(named: "user_agreement_icon_01"), title: "User Agreement".localized), + SPMineItem(type: .helpCenter, iconImage: UIImage(named: "help_center_icon_01"), title: "Help Center".localized), + SPMineItem(type: .aboutUs, iconImage: UIImage(named: "about_us_icon_01"), title: "About Us".localized), + ] + return arr + }() + private lazy var tableView: SPTableView = { + let tableView = SPTableView(frame: .zero, style: .insetGrouped) + tableView.delegate = self + tableView.dataSource = self + tableView.rowHeight = 50 + SPMineCell.registerCell(tableView: tableView) + return tableView + }() override func viewDidLoad() { super.viewDidLoad() - + _setupUI() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.setNavigationNormalStyle() } - - +} + +extension SPMineViewController { + + private func _setupUI() { + view.addSubview(tableView) + + tableView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + +} + +//MARK: -------------- UITableViewDelegate & UITableViewDataSource -------------- +extension SPMineViewController: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = SPMineCell.dequeueReusableCell(tableView: tableView, indexPath: indexPath) + cell.item = dataArr[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataArr.count + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let item = dataArr[indexPath.row] + switch item.type { + case .privacyPolicy: + let vc = SPWebViewController() + vc.urlStr = SPPrivacyPolicyWebUrl + self.navigationController?.pushViewController(vc, animated: true) + + case .userAgreement: + let vc = SPWebViewController() + vc.urlStr = SPUserAgreementWebUrl + self.navigationController?.pushViewController(vc, animated: true) + + default: + break + } + } +} + +extension SPMineViewController { + +// func + } diff --git a/ShortPlay/Class/Mine/Model/SPMineItem.swift b/ShortPlay/Class/Mine/Model/SPMineItem.swift new file mode 100644 index 0000000..058de67 --- /dev/null +++ b/ShortPlay/Class/Mine/Model/SPMineItem.swift @@ -0,0 +1,32 @@ +// +// SPMineItem.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/17. +// + +import UIKit + +struct SPMineItem { + enum ItemType { + ///历史订单 + case orderRecord + ///语言 + case language + ///隐私协议 + case privacyPolicy + ///用户协议 + case userAgreement + ///帮助中心 + case helpCenter + ///关于我们 + case aboutUs + } + + + var type: ItemType? + var iconImage: UIImage? + var title: String? + + +} diff --git a/ShortPlay/Class/Mine/View/SPMineCell.swift b/ShortPlay/Class/Mine/View/SPMineCell.swift new file mode 100644 index 0000000..42fc767 --- /dev/null +++ b/ShortPlay/Class/Mine/View/SPMineCell.swift @@ -0,0 +1,64 @@ +// +// SPMineCell.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/17. +// + +import UIKit + +class SPMineCell: SPTableViewCell { + + var item: SPMineItem? { + didSet { + iconImageView.image = item?.iconImage + titleLabel.text = item?.title + } + } + + private lazy var iconImageView: UIImageView = { + let imageView = UIImageView() + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 14) + label.textColor = .colorFFFFFF(alpha: 0.9) + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + self.contentView.backgroundColor = .colorFFFFFF(alpha: 0.04) + + _setupUI() + + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SPMineCell { + + private func _setupUI() { + contentView.addSubview(iconImageView) + contentView.addSubview(titleLabel) + + iconImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(10) + } + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(40) + } + + } + +} diff --git a/ShortPlay/Class/Player/Controller/SPPlayerDetailViewController.swift b/ShortPlay/Class/Player/Controller/SPPlayerDetailViewController.swift index ad12a5d..3c3ac09 100644 --- a/ShortPlay/Class/Player/Controller/SPPlayerDetailViewController.swift +++ b/ShortPlay/Class/Player/Controller/SPPlayerDetailViewController.swift @@ -23,8 +23,24 @@ class SPPlayerDetailViewController: SPPlayerListViewController { private var detailModel: SPVideoDetailModel? + //MARK: UI属性 + ///选集视图 private weak var episodeView: SPEpisodeView? + private lazy var backButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(named: "arrow_left_icon_01"), for: .normal) + button.addTarget(self, action: #selector(handleBack), for: .touchUpInside) + return button + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontLight(ofSize: 15) + label.textColor = .colorFFFFFF(alpha: 0.9) + return label + }() + override func viewDidLoad() { super.viewDidLoad() self.autoNextEpisode = true @@ -34,6 +50,8 @@ class SPPlayerDetailViewController: SPPlayerListViewController { requestDetailData() _addAction() + + _setupUI() } @@ -56,11 +74,33 @@ class SPPlayerDetailViewController: SPPlayerListViewController { extension SPPlayerDetailViewController { + private func _setupUI() { + view.addSubview(backButton) + view.addSubview(titleLabel) + + backButton.snp.makeConstraints { make in + make.left.equalToSuperview().offset(5) + make.top.equalToSuperview().offset(5 + kSPStatusbarHeight) + make.width.height.equalTo(37) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalTo(backButton.snp.right) + make.centerY.equalTo(backButton) + make.right.equalToSuperview().offset(-15) + } + } + private func _addAction() { self.viewModel.handleEpisode = { [weak self] in self?.onEpisode() } } +} + +extension SPPlayerDetailViewController { + + private func onEpisode() { let view = SPEpisodeView() @@ -93,6 +133,9 @@ extension SPPlayerDetailViewController: SPPlayerListViewControllerDataSource, SP func sp_playerListViewController(_ viewController: SPPlayerListViewController, didChangeIndexPathForVisible indexPath: IndexPath) { self.episodeView?.currentIndex = indexPath.row + let videoInfo = detailModel?.episodeList?[indexPath.row] + + titleLabel.text = String(format: "kPlayerDetailTitleString".localized, "\(videoInfo?.episode ?? 0)", self.detailModel?.shortPlayInfo?.name ?? "") } } diff --git a/ShortPlay/Class/Player/Model/SPSpeedModel.swift b/ShortPlay/Class/Player/Model/SPSpeedModel.swift index 9c88883..7d8ce4d 100644 --- a/ShortPlay/Class/Player/Model/SPSpeedModel.swift +++ b/ShortPlay/Class/Player/Model/SPSpeedModel.swift @@ -7,19 +7,20 @@ import UIKit -struct SPSpeedModel { +class SPSpeedModel: NSObject { enum Speed: String { - case x0_75 = "0.75x" + case x0_5 = "0.5x" case x1 = "1.0x" case x1_25 = "1.25x" case x1_5 = "1.5x" + case x1_75 = "1.75x" case x2 = "2.0x" func getRate() -> Float { switch self { - case .x0_75: - return 0.75 + case .x0_5: + return 0.5 case .x1: return 1 @@ -30,12 +31,53 @@ struct SPSpeedModel { case .x1_5: return 1.5 + case .x1_75: + return 1.75 + case .x2: return 2 } } } + static func getAllSpeed() -> [SPSpeedModel] { + return [ + SPSpeedModel(speed: .x0_5), + SPSpeedModel(speed: .x1), + SPSpeedModel(speed: .x1_25), + SPSpeedModel(speed: .x1_5), + SPSpeedModel(speed: .x1_75), + SPSpeedModel(speed: .x2) + ] + } + var speed: Speed = .x1 + init(speed: Speed) { + super.init() + self.speed = speed + } + + func getRate() -> Float { + return speed.getRate() + } + + func formatString() -> String { + switch speed { + case .x0_5: + return "0.5x" + case .x1: + return "1.0x" + case .x1_25: + return "1.25x" + case .x1_5: + return "1.5x" + case .x1_75: + return "1.75x" + case .x2: + return "2.0x" + } + } + + } diff --git a/ShortPlay/Class/Player/View/SPEpisodeMenuView.swift b/ShortPlay/Class/Player/View/SPEpisodeMenuView.swift new file mode 100644 index 0000000..40d6def --- /dev/null +++ b/ShortPlay/Class/Player/View/SPEpisodeMenuView.swift @@ -0,0 +1,149 @@ +// +// SPEpisodeMenuView.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/17. +// + +import UIKit + +class SPEpisodeMenuView: UIView { + + override var intrinsicContentSize: CGSize { + return CGSize(width: kSPScreenWidth, height: 32) + } + + var didSelectedIndex: ((_ index: Int) -> Void)? + + var dataArr: [String] = [] { + didSet { + self.reloadData() + } + } + + var selectedIndex: Int = 0 { + didSet { + self.buttonArr.forEach { + $0.isSelected = $0.tag == selectedIndex + } + self.progressSlide() + } + } + + + private lazy var buttonArr: [UIButton] = [] + + //MARK: UI属性 + private lazy var progressView: SPGradientView = { + let view = SPGradientView() + view.colors = [UIColor.colorF56490().cgColor, UIColor.colorBF6BFF().cgColor] + view.startPoint = .init(x: 0, y: 0.5) + view.endPoint = .init(x: 1, y: 0.5) + view.locations = [0, 1] + return view + }() + + private lazy var scrollView: SPScrollView = { + let scrollView = SPScrollView() + scrollView.showsVerticalScrollIndicator = false + scrollView.showsHorizontalScrollIndicator = false + return scrollView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + _setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func reloadData() { + buttonArr.forEach { + $0.removeFromSuperview() + } + buttonArr.removeAll() + + let count = self.dataArr.count + + var previousButton: UIButton? + + dataArr.enumerated().forEach { + let normalStrig = NSMutableAttributedString(string: $1) + normalStrig.color = .color9D9D9D() + normalStrig.font = .fontLight(ofSize: 14) + + let selectedString = NSMutableAttributedString(string: $1) + selectedString.color = .colorF564B6() + selectedString.font = .fontMedium(ofSize: 14) + + + let button = UIButton(type: .custom) + button.tag = $0 + button.setAttributedTitle(normalStrig, for: .normal) + button.setAttributedTitle(selectedString, for: .selected) + button.setAttributedTitle(selectedString, for: [.selected, .highlighted]) + button.addTarget(self, action: #selector(handleButton), for: .touchUpInside) + button.isSelected = $0 == selectedIndex + + self.scrollView.addSubview(button) + self.buttonArr.append(button) + + if previousButton == nil { + button.snp.makeConstraints { make in + make.top.left.equalToSuperview() + make.height.equalTo(32) + } + } else if let previousButton = previousButton, count - 1 == $0 { + button.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalTo(previousButton.snp.right).offset(25) + make.height.equalTo(32) + make.right.equalToSuperview() + } + } else if let previousButton = previousButton { + button.snp.makeConstraints { make in + make.top.equalToSuperview() + make.left.equalTo(previousButton.snp.right).offset(25) + make.height.equalTo(32) + } + } + + previousButton = button + } + + progressSlide() + } + + private func progressSlide() { + if self.selectedIndex >= self.buttonArr.count { return } + let currentButton = self.buttonArr[self.selectedIndex] + + self.progressView.snp.remakeConstraints { make in + make.bottom.width.equalTo(currentButton) + make.centerX.equalTo(currentButton) + make.height.equalTo(2) + } + } + + + @objc private func handleButton(sender: UIButton) { + self.selectedIndex = sender.tag + self.didSelectedIndex?(self.selectedIndex) + } +} + +extension SPEpisodeMenuView { + + private func _setupUI() { + addSubview(scrollView) + scrollView.addSubview(progressView) + + scrollView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalToSuperview() + } + } + +} diff --git a/ShortPlay/Class/Player/View/SPEpisodeView.swift b/ShortPlay/Class/Player/View/SPEpisodeView.swift index 8a8a700..9117b6b 100644 --- a/ShortPlay/Class/Player/View/SPEpisodeView.swift +++ b/ShortPlay/Class/Player/View/SPEpisodeView.swift @@ -18,17 +18,49 @@ class SPEpisodeView: HWPanModalContentView { var shortModel: SPShortModel? { didSet { coverImageView.sp_setImage(url: shortModel?.image_url) + titleLabel.text = shortModel?.name + desLabel.text = shortModel?.sp_description } } var dataArr: [SPVideoInfoModel] = [] { didSet { self.collectionView.reloadData() + + var menuDataArr = [String]() + let totalEpisode = dataArr.count + var index = 0 + var remainingEpisodes = totalEpisode + + while remainingEpisodes > 0 { + let minIndex = index * 30 + var maxIndex = minIndex + 29 + if maxIndex >= dataArr.count { + maxIndex = dataArr.count - 1 + } + + let minEpisode = dataArr[minIndex].episode ?? 0 + let maxEpisode = dataArr[maxIndex].episode ?? 0 + + if minEpisode == maxEpisode { + menuDataArr.append("\(minEpisode)") + } else { + menuDataArr.append("\(minEpisode)-\(maxEpisode)") + } + + remainingEpisodes -= 30 + index += 1 + } + + self.menuView.dataArr = menuDataArr } } var didSelectedIndex: ((_ index: Int) -> Void)? + var isDecelerating = false + var isDragging = false + //MARK: UI属性 private lazy var collectionViewLayout: UICollectionViewFlowLayout = { let itemWidth = floor((kSPScreenWidth - 10 * 4 - 30) / 5) @@ -64,6 +96,46 @@ class SPEpisodeView: HWPanModalContentView { return imageView }() + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontMedium(ofSize: 15) + label.textColor = .colorFFFFFF() + return label + }() + + private lazy var desLabel: UILabel = { + let label = UILabel() + label.font = .fontLight(ofSize: 12) + label.textColor = .color9D9D9D() + label.numberOfLines = 0 + return label + }() + + private lazy var lineView: UIView = { + let view = UIView() + view.backgroundColor = .color545454() + return view + }() + + private lazy var menuView: SPEpisodeMenuView = { + let view = SPEpisodeMenuView() + view.didSelectedIndex = { [weak self] index in + guard let self = self else { return } + var row = 0 + if index > 0 { + row = index * 30 + 10 + let count = self.dataArr.count + if row >= count { + row = count - 1 + } + } + let indexPath = IndexPath.init(row: row, section: 0) + self.collectionView.scrollToItem(at: indexPath, at: .centeredVertically, animated: true) + + } + return view + }() + override init(frame: CGRect) { super.init(frame: frame) _setupUI() @@ -107,7 +179,11 @@ extension SPEpisodeView { private func _setupUI() { addSubview(indicatorView) addSubview(coverImageView) - addSubview(self.collectionView) + addSubview(titleLabel) + addSubview(desLabel) + addSubview(menuView) + addSubview(lineView) + addSubview(collectionView) self.indicatorView.snp.makeConstraints { make in make.centerX.equalToSuperview() @@ -123,8 +199,34 @@ extension SPEpisodeView { make.height.equalTo(74) } + self.titleLabel.snp.makeConstraints { make in + make.left.equalTo(self.coverImageView.snp.right).offset(12) + make.right.lessThanOrEqualToSuperview().offset(-15) + make.centerY.equalTo(self.coverImageView) + } + + self.desLabel.snp.makeConstraints { make in + make.left.equalTo(self.coverImageView) + make.right.lessThanOrEqualToSuperview().offset(-15) + make.top.equalTo(self.coverImageView.snp.bottom).offset(8) + } + + self.menuView.snp.makeConstraints { make in + make.left.right.equalTo(self.lineView) + make.bottom.equalTo(self.lineView) + } + + self.lineView.snp.makeConstraints { make in + make.left.equalToSuperview().offset(15) + make.centerX.equalToSuperview() + make.top.equalTo(self.desLabel.snp.bottom).offset(46) + make.height.equalTo(0.7) + } + self.collectionView.snp.makeConstraints { make in - make.edges.equalToSuperview() +// make.edges.equalToSuperview() + make.left.right.bottom.equalToSuperview() + make.top.equalTo(self.lineView.snp.bottom).offset(15) } } @@ -150,4 +252,50 @@ extension SPEpisodeView: UICollectionViewDelegate, UICollectionViewDataSource { } } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if isDragging || isDecelerating { + updateMuneSelectedIndex() + } + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + isDecelerating = false + updateMuneSelectedIndex() + } + + func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { + isDecelerating = true + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + isDragging = true + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + isDragging = false + } + + + func updateMuneSelectedIndex() { + let indexPathArr = collectionView.indexPathsForVisibleItems + + var minRow = dataArr.count - 1 + var maxRow = 0 + + for indexPath in indexPathArr { + if indexPath.row < minRow { + minRow = indexPath.row + } + if indexPath.row > maxRow { + maxRow = indexPath.row + } + } + + let selectedIndex = maxRow / 30 + if menuView.selectedIndex != selectedIndex { + menuView.selectedIndex = selectedIndex + } + } + } diff --git a/ShortPlay/Class/Player/View/SPPlayerControlView.swift b/ShortPlay/Class/Player/View/SPPlayerControlView.swift index 28c2158..8e16b61 100644 --- a/ShortPlay/Class/Player/View/SPPlayerControlView.swift +++ b/ShortPlay/Class/Player/View/SPPlayerControlView.swift @@ -39,6 +39,9 @@ class SPPlayerControlView: UIView { } } + var durationTime: Int = 0 + var currentTime: Int = 0 + var isCurrent: Bool = false { didSet { updatePlayIconState() @@ -46,6 +49,17 @@ class SPPlayerControlView: UIView { } //MARK: UI属性 + ///底部渐变层 + private lazy var bottomGradientView: SPGradientView = { + let view = SPGradientView() + view.colors = [UIColor.color000000(alpha: 0).cgColor, UIColor.color000000(alpha: 0.5).cgColor] + view.startPoint = .init(x: 0.5, y: 0) + view.endPoint = .init(x: 0.5, y: 1) + view.locations = [0, 1] + view.isUserInteractionEnabled = false + return view + }() + private(set) lazy var progressView: SPPlayerProgressView = { let view = SPPlayerProgressView() view.panStart = { [weak self] in @@ -97,6 +111,7 @@ class SPPlayerControlView: UIView { NotificationCenter.default.addObserver(self, selector: #selector(updateShortCollectStateNotification), name: SPVideoAPI.updateShortCollectStateNotification, object: nil) let tap = UITapGestureRecognizer(target: self, action: #selector(hadlePlayAndOrPaused)) + tap.delegate = self self.addGestureRecognizer(tap) @@ -136,10 +151,16 @@ class SPPlayerControlView: UIView { extension SPPlayerControlView { private func sp_setupUI() { + addSubview(bottomGradientView) addSubview(progressView) addSubview(playImageView) addSubview(rightFeatureView) + bottomGradientView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.height.equalTo(kSPTabbarSafeBottomMargin + 200) + } + progressView.snp.makeConstraints { make in make.left.equalToSuperview().offset(10) make.centerX.equalToSuperview() @@ -236,3 +257,15 @@ extension SPPlayerControlView { self.panProgressFinishBlock?(progress) } } + +//MARK: -------------- UIGestureRecognizerDelegate -------------- +extension SPPlayerControlView: UIGestureRecognizerDelegate { + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + if touch.view != self { + return false + } else { + return true + } + } +} diff --git a/ShortPlay/Class/Player/View/SPPlayerDetailControlView.swift b/ShortPlay/Class/Player/View/SPPlayerDetailControlView.swift index 65ba386..71b832c 100644 --- a/ShortPlay/Class/Player/View/SPPlayerDetailControlView.swift +++ b/ShortPlay/Class/Player/View/SPPlayerDetailControlView.swift @@ -10,12 +10,74 @@ import UIKit class SPPlayerDetailControlView: SPPlayerControlView { + override var durationTime: Int { + didSet { + updateProgressTimeLabel() + } + } + + override var currentTime: Int { + didSet { + updateProgressTimeLabel() + } + } + + override var viewModel: SPPlayerListViewModel? { + didSet { + self.viewModel?.addObserver(self, forKeyPath: "speedModel", context: nil) + updateSpeedButton() + } + } + + override var isCurrent: Bool { + didSet { + if isCurrent { + showSpeedSelectedView(isShow: false) + } + } + } + + + //MARK: UI属性 private lazy var episodeButton: UIButton = { let button = createFeatureButton(title: "Episodes".localized, image: UIImage(named: "episodes_icon_01")) button.addTarget(self, action: #selector(handleEpisodeButton), for: .touchUpInside) return button }() + private lazy var progressTimeLabel: UILabel = { + let label = UILabel() + label.font = .fontLight(ofSize: 12) + label.textColor = .colorFFFFFF(alpha: 0.9) + return label + }() + + private lazy var speedButton: UIButton = { + let button = UIButton(type: .custom) + button.setBackgroundImage(UIImage(color: .colorFFFFFF(alpha: 0.2)), for: .normal) + button.setTitleColor(.colorFFFFFF(alpha: 0.9), for: .normal) + button.titleLabel?.font = .fontLight(ofSize: 11) + button.layer.cornerRadius = 10 + button.layer.masksToBounds = true + button.addTarget(self, action: #selector(handleSpeedButton), for: .touchUpInside) + return button + }() + + private lazy var speedSelectedView: SPSpeedSelectedView = { + let view = SPSpeedSelectedView() + view.isHidden = true + view.didSelectedSpeed = { [weak self] speedModel in + guard let self = self else { return } + self.viewModel?.setSpeedPlay(speedModel: speedModel) + self.showSpeedSelectedView(isShow: false) + } + return view + }() + + deinit { + self.viewModel?.removeObserver(self, forKeyPath: "speedModel") + } + override init(frame: CGRect) { super.init(frame: frame) @@ -26,6 +88,25 @@ class SPPlayerDetailControlView: SPPlayerControlView { fatalError("init(coder:) has not been implemented") } + override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) + if keyPath == "speedModel" { + updateSpeedButton() + } + + } + + private func updateProgressTimeLabel() { + let currentTime = self.currentTime.formatTimeGroup() + let durationTime = self.durationTime.formatTimeGroup() + + progressTimeLabel.text = "\(currentTime.1):\(currentTime.2)/\(durationTime.1):\(durationTime.2)" + } + + ///更新速率按钮 + private func updateSpeedButton() { + self.speedButton.setTitle(self.viewModel?.speedModel.formatString(), for: .normal) + } } extension SPPlayerDetailControlView { @@ -33,6 +114,34 @@ extension SPPlayerDetailControlView { private func _setupUI() { self.rightFeatureView.addArrangedSubview(episodeButton) + addSubview(progressTimeLabel) + addSubview(speedButton) + addSubview(speedSelectedView) + + self.progressView.snp.remakeConstraints { make in + make.left.equalToSuperview().offset(15) + make.centerX.equalToSuperview() + make.height.equalTo(30) + make.bottom.equalToSuperview().offset(-(kSPTabbarSafeBottomMargin + 10)) + } + + self.progressTimeLabel.snp.makeConstraints { make in + make.left.equalTo(self.progressView) + make.bottom.equalTo(self.progressView).offset(-12) + } + + speedButton.snp.makeConstraints { make in + make.centerY.equalTo(self.progressTimeLabel) + make.right.equalToSuperview().offset(-15) + make.width.equalTo(40) + make.height.equalTo(20) + } + + speedSelectedView.snp.makeConstraints { make in + make.left.right.equalToSuperview() + make.bottom.equalTo(self.speedButton.snp.top).offset(-30) + } + } } @@ -41,4 +150,18 @@ extension SPPlayerDetailControlView { @objc private func handleEpisodeButton() { self.viewModel?.handleEpisode?() } + + @objc private func handleSpeedButton() { + showSpeedSelectedView(isShow: speedSelectedView.isHidden) + } + + ///暂时或隐藏速率选择页面 + private func showSpeedSelectedView(isShow: Bool) { + speedSelectedView.isHidden = !isShow + rightFeatureView.isHidden = isShow + + if isShow { + self.speedSelectedView.currentSpeed = self.viewModel?.speedModel.speed ?? .x1 + } + } } diff --git a/ShortPlay/Class/Player/View/SPPlayerListCell.swift b/ShortPlay/Class/Player/View/SPPlayerListCell.swift index 1d543c1..6f12ca7 100644 --- a/ShortPlay/Class/Player/View/SPPlayerListCell.swift +++ b/ShortPlay/Class/Player/View/SPPlayerListCell.swift @@ -91,6 +91,9 @@ class SPPlayerListCell: SPCollectionViewCell, SPPlayerProtocol { didSet { self.controlView.progress = 0 self.coverImageView.isHidden = false + self.controlView.currentTime = 0 + self.controlView.durationTime = 0 + self.controlView.videoInfo = videoInfo player.setPlayUrl(url: videoInfo?.video_url ?? "") @@ -169,6 +172,8 @@ extension SPPlayerListCell: SPPlayerDelegate { func sp_playTimeChanged(_ player: SPPlayer, currentTime: Int, duration: Int) { controlView.progress = CGFloat(currentTime) / CGFloat(duration) + controlView.currentTime = currentTime + controlView.durationTime = duration } func sp_firstRenderedStart(_ player: SPPlayer) { diff --git a/ShortPlay/Class/Player/View/SPPlayerProgressView.swift b/ShortPlay/Class/Player/View/SPPlayerProgressView.swift index f538525..ae8c59f 100644 --- a/ShortPlay/Class/Player/View/SPPlayerProgressView.swift +++ b/ShortPlay/Class/Player/View/SPPlayerProgressView.swift @@ -32,10 +32,10 @@ class SPPlayerProgressView: UIView { ///滑动进度 private var panProgress: CGFloat = 0 - var progressColor: UIColor = .red - var currentProgress: UIColor = .white + var progressColor: UIColor = .colorFFFFFF(alpha: 0.12) + var currentProgress: UIColor = .colorFFFFFF(alpha: 0.48) - var lineWidth: CGFloat = 2 + var lineWidth: CGFloat = 5 ///是否在滑动中 private var isPaning: Bool = false diff --git a/ShortPlay/Class/Player/View/SPSpeedSelectedCell.swift b/ShortPlay/Class/Player/View/SPSpeedSelectedCell.swift new file mode 100644 index 0000000..bad7013 --- /dev/null +++ b/ShortPlay/Class/Player/View/SPSpeedSelectedCell.swift @@ -0,0 +1,56 @@ +// +// SPSpeedSelectedCell.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/17. +// + +import UIKit + +class SPSpeedSelectedCell: SPCollectionViewCell { + + var model: SPSpeedModel? { + didSet { + textLabel.text = model?.formatString() + } + } + + var sp_isSelected: Bool = false { + didSet { + if sp_isSelected { + self.contentView.layer.borderColor = UIColor.colorF564B6().cgColor + self.contentView.backgroundColor = .colorF56490(alpha: 0.2) + self.textLabel.textColor = .colorF564B6() + } else { + self.contentView.layer.borderColor = UIColor.clear.cgColor + self.contentView.backgroundColor = .colorFFFFFF(alpha: 0.2) + self.textLabel.textColor = .colorD2D2D2() + } + + } + } + + private lazy var textLabel: UILabel = { + let label = UILabel() + label.font = .fontLight(ofSize: 14) + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + self.contentView.layer.cornerRadius = 7 + self.contentView.layer.masksToBounds = true + self.contentView.layer.borderWidth = 1 + + contentView.addSubview(textLabel) + + textLabel.snp.makeConstraints { make in + make.center.equalToSuperview() + } + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} diff --git a/ShortPlay/Class/Player/View/SPSpeedSelectedView.swift b/ShortPlay/Class/Player/View/SPSpeedSelectedView.swift new file mode 100644 index 0000000..7b07855 --- /dev/null +++ b/ShortPlay/Class/Player/View/SPSpeedSelectedView.swift @@ -0,0 +1,93 @@ +// +// SPSpeedSelectedView.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/17. +// + +import UIKit + +class SPSpeedSelectedView: UIView { + + override var intrinsicContentSize: CGSize { + return CGSize(width: kSPScreenWidth, height: 54) + } + + var didSelectedSpeed: ((_ model: SPSpeedModel) -> Void)? + + var currentSpeed = SPSpeedModel.Speed.x1 { + didSet { + self.collectionView.reloadData() + } + } + + private lazy var dataArr: [SPSpeedModel] = SPSpeedModel.getAllSpeed() + + + //MARK: UI属性 + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.itemSize = .init(width: 70, height: 54) + layout.minimumLineSpacing = 10 + layout.minimumInteritemSpacing = 10 + layout.sectionInset = .init(top: 0, left: 15, bottom: 0, right: 15) + return layout + }() + + private lazy var collectionView: SPCollectionView = { + let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.showsVerticalScrollIndicator = false + collectionView.showsHorizontalScrollIndicator = false + SPSpeedSelectedCell.registerCell(collectionView: collectionView) + return collectionView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + _setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SPSpeedSelectedView { + + private func _setupUI() { + addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } + +} + +//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource -------------- +extension SPSpeedSelectedView: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let model = dataArr[indexPath.row] + let cell = SPSpeedSelectedCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath) + cell.model = model + cell.sp_isSelected = model.speed == currentSpeed + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return dataArr.count + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + + self.didSelectedSpeed?(dataArr[indexPath.row]) + + } + +} diff --git a/ShortPlay/Class/Player/ViewModel/SPPlayerListViewModel.swift b/ShortPlay/Class/Player/ViewModel/SPPlayerListViewModel.swift index b6e4c36..0e3a63b 100644 --- a/ShortPlay/Class/Player/ViewModel/SPPlayerListViewModel.swift +++ b/ShortPlay/Class/Player/ViewModel/SPPlayerListViewModel.swift @@ -22,18 +22,19 @@ class SPPlayerListViewModel: NSObject { self?.handlePlayFinish?() } _currentPlayer?.isCurrent = true + _currentPlayer?.rate = speedModel.getRate() } get { return _currentPlayer } } - private(set) var speed: SPSpeedModel.Speed = .x1 + @objc dynamic private(set) lazy var speedModel = SPSpeedModel(speed: .x1) ///设置倍速播放 - func setSpeedPlay(speed: SPSpeedModel.Speed) { - self.speed = speed - currentPlayer?.rate = speed.getRate() + func setSpeedPlay(speedModel: SPSpeedModel) { + self.speedModel = speedModel + currentPlayer?.rate = speedModel.getRate() } diff --git a/ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/About Us@2x.png b/ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/About Us@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..efea7c3fb9aa4f731311f6e72a298ac971f1958c GIT binary patch literal 1738 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 literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/Contents.json new file mode 100644 index 0000000..58cc5d3 --- /dev/null +++ b/ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "About Us@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "About Us@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShortPlay/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Contents.json new file mode 100644 index 0000000..2fedf34 --- /dev/null +++ b/ShortPlay/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "nav_back@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShortPlay/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/nav_back@2x.png b/ShortPlay/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/nav_back@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..633b9e6de011889e7797b505c67d2df72ada89ff GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^qCl*|!2%@DSnLFGtUX;ELn;{Gp4!TIz(9ay!|gwH zpB8L13U=5ku)SD)f|$NfkCpo(<=n^e|6i=Cn0jE>&-3+S3iD!n(na~7?p>EK^WnWa QKw}s@UHx3vIVCg!0JBypJ^%m! literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Contents.json new file mode 100644 index 0000000..f6a1eb0 --- /dev/null +++ b/ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Help Center@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Help Center@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Help Center@2x.png b/ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Help Center@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3f11205f002fdabfff330c253d4c5d9a13153052 GIT binary patch literal 1625 zcmV-f2B!ImP)Px*6G=otR9Hvtn15`{XB@}hpXa{a4j1ld&(WfXY?T(hwo0e7URD_~8zDksNt-R1 zWs)_~Wfm4)LRgSlVnMdWWHKU_Y)EuAxh8a~EiG*gW8a3NPV1^p-RAE3?7nTUT0ib* z|FFdK&*knu&*yo4-k<0BJUmt$PM5)Gwr|U!A~X=wM7FQF`%llexyaNfJ8A9jNtAtgM1R8!EL(k z6q~J9@}IaNaLmEg3_j)Z0+`W8|i^?2R2CX=aCC6~)()GLN`2=O{IzsC%V zX3yYyU7WH|ucr&)LWT|4-u_@ZF-p5r*(QWoSy?TGAuy1>)**<#&Hdf2(@16ojYvUz@~5p2-vi?b|fi6ImnE30B(iiat$Ur z6Us+OK&R7bP;^7;z(@vBh1b_M(`+_t3n1dA`HB-m91LUuxa5_0Y@vLG1Z=(K*(e#s z09qK{iDt8*IbyP29h^?Lg;=QrAs&QH2?_d-gZT>zsH$$u0KHBnK+nWWES7!<-Z-49SQB5}~? zPxBoVu<7c(I%ZB2pyi392YubIfLA*{@`1O2xqj4W!^DUIRn=t_q`0(>lD$@|)zsLZ zfU4?B7Nn@%{!LbER}Fo%JypTWm)moRa74DEo?r}?+T?I|h74??E+{CU^glJPSDuOQkY zC4SZEXu8iQSA(DsXxW4b#vP%QdeZ;r&fWPCB)4k5bwaH5`M6vNnj={jY4(CJFD1qF zOYgpq&B6Nm+j{^kCc^gA)S*2QH8i-^Fmnljvb40J_Gkd>8(hc0oJ&L{si~%((o)~x z+6m?Y0DIHYhAs&Tc((4ggPHRrxX?B!{`-LO*|RPA4EhZ~ZMRo%)X0S6H^W@f}k zrQoU4Eh;l7vxI%hl!t()PB}Xj1feV|BOy3oWtCGqZFDM_ll>6zMAeNv0i6Sc#;IB6 zr~oUgohmbuCCHxB3$U`%*`*+%EIZ2_9Pq?R=Rq*P$>0sIOtX9%NJB;C%@r)M351HA zsl#)knS&K4Z`Lr7PK4rV*~2#neEd<-yq<_w0(2;6s(EhEMN(dVeJPXp0bmaxiimxs zER;NOXL-;LtgENzSe{r^LqV6HaCR4exIw&}B$qMiW*Dk(1D3#V0sjP)&d41xJ7l5O3^Yig zq^#<`@FZ#bu^_Qq`dUA;+f$=DO*DbeBLE+1Z6;6L;BH*CITgC{}V*Bk7ePk{88HNFsv4{f~7~A{$$7$K~y5W-d%xFMO@r^ zDdl1f+P`Yu1Z?ead%u`J-x|>iL(G7OO0WDyz<5DiF#C-$J%EQwuYBo$QP9$Uyrcc@ z>_v;Bo4YXs9yokuBcv?u^U4#*wk9WY;ahX8Jv&lZdF^EsGvK~`4Tcv74*EtiFYa5v zw1X~fnm2b$PqB{_u3r{OV9*~AT!?MVT|tY9>%MAGLuU?gh&FH`&< X;n8wOBUV(c00000NkvXXu0mjf@yrDN literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Help Center@3x.png b/ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Help Center@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..955c51e8de51e68bdda68fb5c9380d8c3efe47a0 GIT binary patch literal 3034 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 br3d^MQHfl1wn#6s00000NkvXXu0mjf{|$7? literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Language@3x.png b/ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Language@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..4be9fd4679b7768a6cf740ee4221b6c83e795b68 GIT binary patch literal 5377 zcmV+c75?gpP)Px}yGcYrRA@u(nt7O9Rh7W+^6uMeuj(xU5(GkTRrNYyF$5hA!iOK?j3c0+5@2M$ z!2n7CVc(_5g~+O02x4Y003tUAcO;O?s_f@ z8c<5P<2ux~?VYY`J>h!pIzq_WMT-_W2Lk+n0O#}hHc=F>kA!0%jz$v+1R*hMRnIUg zzE6EaBqC{QoF9b%@PVP-)o3&p4U8|BOQn|P`BA_D8W`GD@qE|ANC=0+Q6U_P5YD+* zt`v(^qxyT>Hm~mL>U!-@h5nO(w{G1U^?m<*QIxN2NVPN}2wBann#JO9o^kdh0N@!# zR&PrsnvO#l^PTT(dyZ1Rt+{Fbxj{@1483z7g!qUB2Y=urj59PmGWde)T9-1$PDBWO zDwSx;sA^c{oS~tSfkDsnzf~%Y+_!Y;(#rmV?>F$4En74I(B+9(>P$sZ1;eNoYi9K+ z+cDQN#(t?N3S~aKy}9||vlwG=q&WEfYPGyt6y-A-8(P)`F+Di^&MMCNBeEo~Zb;4h z5`+ls9(e2FN~L`9ym|9*tycRaA<{`sn_Cg792hTbmIKPhHnnHl*g=qDZPZG`#BG`fXS{!`Zw!uZ3cEsR>@=cpI zElehwewK(gE-sb^-!!f2866!Rn}Xj1;8ZdIW?*=9_ZV>SeP3VSQdNyUl87}PQY;U3 zkB$zWwq(hY9TPHNfBp3)=EFM^$);0?ASmVXh;G{UdF|~-ygC=?IROWrRn_R4a46AX z+a|44hpuCc-I-3OM<*-o>)U)~BHs8-2oW0?eCuva)4nqCdjOn_H?84-gVEx|YVBE*0^6)k7?X5RXmfPRopJEJL9FH|#B}s~4R7d+9&%9(Vo}M+Zu5Zy) zMg3tcnmWp~Yks9Na)Bt~hxVN=Hg4RghC-2@jj4l^Bcr>E2=hZ1En2iE$ppahc+(mH zI4JG|ynXxjxa<168j=SoL&G~qDf1V0c6J6sWY4p1-8#9kvE}S|tl4v}(Yc!O!0 zD>IqdjpkQVqJC0U!m{VP#IcRM8cK9h>hY>E^3{%xwp(W! z&ocrCMTI1Vo{vSEno5;{Aj#GQXc4{?MNwkMTQMFjw z^*p8Cm5D^6_n?CgGXFS+^_9^`5Ypg%z?^g3)6X-%Bv<0N|?$5tpfIyagj7Lx|vb`N*hkmn*Jk5ypHG02m}S8$w`n4oNi9 zurR2hjoRp&5CW5PAOHX)&e=kQkR^#BUs2*w2qES{Ff+=1-?2@r{I2J@8wf$yx3#rx z8PEFtC#!1gVMz=nE9KpfZ{M-)3#XiNO5HMaTHr#VaH^st?vIqQkLwBAxN+mWup0egIMVX*s`2idl)4W< zNK^p01|e<&fF&WsKIg0@9BNPlIYw=y0st#>4r%~^cNt@S5c16s@?pU7>R4=kYtt`!U!<|G>q?$Q!Td_ENZM{`iUEuES{zFw&eO8eZA1GILzQ8&C zWHy^E)&tgceE}ii|B|J6Yq_%Xm25V1>{M-oq@^m2V@b=jhTfN@C;Ai$g+D|iha6)& z<(;-&`iQ1!+w1Axv13P~QZarRjU0TuZ5OvuW}eW|(Xpp!ncS}~aG}tDsw~Byga|^* z8oDZz$=p0$GU(~)KT=UbuV5@iO>^j`Y&QL`^?+r$VQe0@tl_%A`MmxeRc*SIQMcxL zrQ_4-bZ$ERy}iAciDL3wfOBX&rB7ut9lx5YeO+K(-~70uG<@1|il*;dM`yFy&C@B= zb^UlkV(WrIXgkF(Xqxu3dccyD8q3nQT_3oor{`=%N&N^y=kteQ ztQKS`rr6HNPdhu?K09mRU|XiKasGfTHzsX+a929rcF;`oTVG$_*%-&}q11AH-~3>` z7ZilFD5ll~J07lEA2^rGEhQxKv>=4Sjx+rAOs4aOnQYMYEw9UR!{LrIJUlqKYu;4b zMN@iM*YyqvmFGngNxI(14>~*BFPKSLZ*Tuq0#2-^e#LC>T+|35^y+z-kZ}*Yfrqv6 z#lybO)~#C`Jr8UZgjmdVNA5|d+y7-I8+3j1jf5mFqtqyQp79|~)5fN*8OLxwpFe|; z_)j3jp6?egOQ+L!&8)2dc0q_=Hu@jQK7Kem81sj zc_SOr>9*r%vY}Au{R|=TA2H?u*DZantE=mM!(d7e_w*Dllcdxw<8z{MS|-!})J)2B zz5gDJWB*L4@rQK!$PZ2jjB#=-OMBjEUErX9q43)4f)GF4_eVEoGHu7qY=eFbgpmyp z5yAI{FU@AN-<_3*3kCf_j8iK(_Z-eEw*dgAO!1S2aW0&LaqL*e%5MSS-&D^y&f#$w zCq5JW?)xK;Ap~En-&VK=;hSLy43;^E?!f($h)Od9`{_OP`V+aFacN}=iHbLLuk;u46 z#|vuZz2!`n|DH_X`GEV=$owPLI50zujXajkcD`?FnsN;1a{7Z1B~}3LS)5nD4FDLJ z(ZP^h4Pg9W&WgJL@W&^0=zhF(#q$zyuv}`XuvDaYg*e!XR<-p^U(y5o_Vl;v`pU*;foEC5CS$tNC_Rcn9XLV+&&F}DT|JU zG)DD-3xz^ErP7NG$P$K?3p6eL;7m4j=lXU~npi+^WiXpPa{lCX^F-gei-(^4=0|-m zat6kRNd#}k)k-d>pXt-^{Rnc5P~{v=(|%Gfqyu2i01$-wz`0!Rbf1QQ3}J8_S3aSu zt84vqz`0zmm2qhkV-Xpn@)%OBS?hG4P9O7YiCfn#Angx z7{d`!DBrAUnJel6dwyih!$hdm4e6eq{7oK>U4ejK#<+UyOb2QCyndzQM!ty<_XTWx zO4HK6nG^7)O??Xi5ZAhX=m=RVy$peSx~6Gw*Mlw;3Wqzcy56Tsiz1D@plO*8)dP0D zabS_u2hQgUFIaYR85XS9WYIn;old`1KlA6B03iQqJBg)4Ft&=Kb7H;rbwhgl_U-a$ z$@+%p#VdzAPHQk|gh(eUDXgx!~>M z4?@V6d7k_ajvH=6*flZoMaBd-;Nlz+KE6daRz`6yjNJf0X#94=*c1hWI}qFoj>!?C zBIU%Pz7Uc~wT}?@7o39)5VAjLnzrW*E9l$Z-5qC4xySLMr;EhgC5i5NnM}uj*NcW} zcP)E+dqvmf57eyKS%N^z5-~1cyg2>CK$x!UNlN*(oTHO%M`>h?BpF8_81n&`Ab3>3 zPEEiJalm{4xCkK(wohHt3a5g-Ohu{{IpARkxWE{$0D!PhafmWPC?(@B12PUF0~mRu zifj!6z<&!N`^LonT2G<(3$~@+;rl`?6skNd3gEoX&Q5o~fZuzAhcYg|STkeIA+-#E zNRLDwUd{oy&a#ze#t27{1Bl#`EV`pE6O+Cx#zJc4B?P^jg3Brx^mRBFeJ)rtga_o<}56bWH(y{gUK8 zLn(Vn6!DwB&+jN#W5>&qJwycK_{EDC?{yLo02{T?Sm6;4jRQ|MaO1{}hch1iz3nLT zqT$jDock96fDeTb9q0Sv$2>~fT$iXsa2<@jcWqZWlwjYHC41G9B^^`lo=lcME8t+W z+qS{&h8bPKIRX$-8dA)nNa!k2#Lw;Cy?bzC|0t*H=bMJ|J;o7MRO^x@+TKP4z*R#X z%hHkX`+;-0!k3I%=r)9yuPU`KYnnDTggECyE|+Tt05A7_@w+ukiE$pRrWlAts`nGZ zFWL8`an4C=a))4_%QVHi4O96rA)Xsn&5KH2Me3G6F!@w3 z0InG7*d|^qvN!PN&6^{J$@{9+@Ilc?_1&YEbj;t^=wS9Jm(3LwL;0RhMKP=zzf)xI zOPNfjf4Wef_-$4mo@kuU=Z~w|@*SoncZq`MhgI_iSr+ctcgpDQ*3UOg_1_r-EFP`i zrD@tF6EXteaz$MeWaWK<^ZERDiskSH5OOIRwlD1J>iqsb;og9d{AI&buJ9-!A=&9x zWcNZ%>wI}Ou;Vkr9G{+^-o>UxepIvMPK=lt4cC56h;-rPD-Xe)jjBO@TCK^)heGx? zMf6TzytuO`P!#|d%gUPI$SEHEqla}}*Q!fGriNo>k z?%et1YWP;i735^B_INakFKTUV9S(p;%gR{Nip8u|OSH`0TefUzs8r}3#Y*UOLVPqIYg0&y#ds_Z=_ifk>$;3){9>p1Mz;bQ0<1pjx*AGK~vB;;=xhV}W8lDsB()3La< zN`U+k&lA2@s>l}y^LRtbc%W7-U7AXzMBAmOdJa0#a>P$qwh)&@--(2rM+61j($(47 zJ6G8K3hBv8)~{b5QIz;Ol;LYeMx|x|5tAj)j797MLi`sP!?mU*e5X{AmT?YQOH*wf z1bo}@s4}*jn2cNZ0RRpk7!EH-ki&S?S+A<}3QFOLjPX-TRXJU1bUq+ z!v|)QjrY6on9W<%b^Qq22H!Gm@$XEFBq#+$kv%V@dLF{i^n4@~OLB1Ckfq{QxmpwB zf!uJ&Esd6xSg;q6O4t=4xM@j&VpxRO4j~9~FQm95A(j4)bL;A(k3Ra1+1N8(oBbNY zQwe8GW6^A7$PrDVD7z)!m#THtyZQyN?)1Jw5+Vl4O^>{E_!@B|eaV-P^ZIT3hp zDF9>4xfm3dguNw6^a)v{&ti<9nXX)%%S$+5z!OS0Y}la0G)J@&}=U3wEhA_<#2LaJ{!2R7kzOy+?A+ z)0Faq7z0|cZ~!1!Xav`XK^|lHc2IK<4Gq2a7k5E?F5&g(XnVjz`p*F|7oC3tp3B?% fOKkgB=i&bWk~Y!P+3`Bw00000NkvXXu0mjfngUnF literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Contents.json new file mode 100644 index 0000000..3760e29 --- /dev/null +++ b/ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Order Record@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Order Record@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Order Record@2x.png b/ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Order Record@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7c0c4ab0446b7ab0e5cadaf99a48cae3213439ff GIT binary patch literal 1793 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 literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/Contents.json new file mode 100644 index 0000000..c9f3912 --- /dev/null +++ b/ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "play@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "play@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/play@2x.png b/ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/play@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..14317dd13b242396013ae8edad732a99a4cad672 GIT binary patch literal 280 zcmV+z0q6dSP)Px#(n&-?R5(wy)IAOXK^VsI|D)tADtF)_KJK8BprBT{2_=a}q7aGN9c&@U*hyw( z!?1RDOfkt6^P8E>`#vJ@P+*TY&=^wznm{CJo%cW=I0rfoS^zysr(aUkhBW{-reWel6sD!Hw^YEwrobgBt+R1E^OOswUDis4u>VsEgKFtzFIE6R#{Px$X-PyuR7gwBmpv~9K^TRf6QzcVMEnsNrJtcu2@#2iC}@O4r_i8K(-4VJ(9jZ{ zitD2j4|#LEpeVumJ2zazzSA13yIy2bz4`)Ncc8K<&Q; zq=B;(T>%@h`g*(Si-3*w0UQIG*H(&xd0_u5|zbvp7U`K5qSeJB>rM^tC zvFyA*16#nVr1w?@lnqWeR=_DRDe0yeyee?Qx&%fg*(&@QRf7#SCTS-GcwCbGbFYeT zfe}fMzQE>sXTXf4Yag2{$Z;|QIfxD9+?RovWljNyzAQ6Hlc=mMTALdT5d@ZjxrXRt z#mobHd0JoE21~Qd{%GP-6!L}|!roohj?MEtHCM;+wl{8fFJ9tpo~nzy)0sDkB8sx+ Z{sM(ss2}K)_uv2k002ovPDHLkV1fqgwwC|^ literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Contents.json new file mode 100644 index 0000000..8a1e1bf --- /dev/null +++ b/ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Privacy Policy@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Privacy Policy@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Privacy Policy@2x.png b/ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Privacy Policy@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..50e814f29afb6ed2d28201bea1d6912e5d2ca3ba GIT binary patch literal 2027 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 literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/en.lproj/Localizable.strings b/ShortPlay/Source/en.lproj/Localizable.strings index e31b88f..7b3ed2e 100644 --- a/ShortPlay/Source/en.lproj/Localizable.strings +++ b/ShortPlay/Source/en.lproj/Localizable.strings @@ -20,3 +20,13 @@ "Episodes" = "Episodes"; "Save" = "Save"; "Added" = "Added"; +"Series" = "Series"; +"Order Record" = "Order Record"; +"Language" = "Language"; +"Privacy Policy" = "Privacy Policy"; +"User Agreement" = "User Agreement"; +"Help Center" = "Help Center"; +"About Us" = "About Us"; + +///视频详情标题 +"kPlayerDetailTitleString" = "Episode %@ / %@"; diff --git a/ShortPlay/Thirdparty/JXButton/JXButton.swift b/ShortPlay/Thirdparty/JXButton/JXButton.swift index dbf2ae8..e1a8929 100644 --- a/ShortPlay/Thirdparty/JXButton/JXButton.swift +++ b/ShortPlay/Thirdparty/JXButton/JXButton.swift @@ -26,8 +26,36 @@ class JXButton: UIButton { private var titleRect: CGRect = .zero - override func layoutSubviews() { - super.layoutSubviews() + override class var layerClass: AnyClass { + return CAGradientLayer.self + } + + var gradientLayer: CAGradientLayer { + return self.layer as! CAGradientLayer + } + + var locations: [NSNumber]? { + didSet { + self.gradientLayer.locations = locations + } + } + + var colors: [CGColor]? { + didSet { + self.gradientLayer.colors = colors + } + } + + var startPoint: CGPoint = .zero { + didSet { + self.gradientLayer.startPoint = startPoint + } + } + + var endPoint: CGPoint = .zero { + didSet { + self.gradientLayer.endPoint = endPoint + } } override var intrinsicContentSize: CGSize {