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 0000000..efea7c3 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/About Us@2x.png differ diff --git a/ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/About Us@3x.png b/ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/About Us@3x.png new file mode 100644 index 0000000..b84fc69 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/about_us_icon_01.imageset/About Us@3x.png differ 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 0000000..633b9e6 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/arrow_left_icon_01.imageset/nav_back@2x.png differ 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 0000000..3f11205 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Help Center@2x.png differ 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 0000000..955c51e Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/help_center_icon_01.imageset/Help Center@3x.png differ diff --git a/ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Contents.json new file mode 100644 index 0000000..20461fd --- /dev/null +++ b/ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Language@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Language@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Language@2x.png b/ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Language@2x.png new file mode 100644 index 0000000..616ea5c Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Language@2x.png differ 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 0000000..4be9fd4 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/language_icon_01.imageset/Language@3x.png differ 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 0000000..7c0c4ab Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Order Record@2x.png differ diff --git a/ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Order Record@3x.png b/ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Order Record@3x.png new file mode 100644 index 0000000..05b3d25 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/order_record_icon_01.imageset/Order Record@3x.png differ 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 0000000..14317dd Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/play@2x.png differ diff --git a/ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/play@3x.png b/ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/play@3x.png new file mode 100644 index 0000000..a99f806 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/play_icon_02.imageset/play@3x.png differ 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 0000000..50e814f Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Privacy Policy@2x.png differ diff --git a/ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Privacy Policy@3x.png b/ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Privacy Policy@3x.png new file mode 100644 index 0000000..266aab3 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/privacy_policy_icon_01.imageset/Privacy Policy@3x.png differ diff --git a/ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/Contents.json new file mode 100644 index 0000000..481be51 --- /dev/null +++ b/ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "User Agreement@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "User Agreement@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/User Agreement@2x.png b/ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/User Agreement@2x.png new file mode 100644 index 0000000..2f24541 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/User Agreement@2x.png differ diff --git a/ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/User Agreement@3x.png b/ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/User Agreement@3x.png new file mode 100644 index 0000000..2658c6b Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/user_agreement_icon_01.imageset/User Agreement@3x.png differ 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 {