From 7ff597e1eedbc3ae1afe90177a3cebb11fb62e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=BE=E8=A7=89=E6=96=B0?= Date: Sat, 19 Apr 2025 09:20:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E8=97=8F=E5=88=97=E8=A1=A8=EF=BC=8C?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E8=AE=B0=E5=BD=95=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Base/Controller/SPTabBarController.swift | 4 +- .../Base/Controller/SPViewController.swift | 4 + ShortPlay/Base/Extension/UIColor+SPAdd.swift | 8 ++ .../Extension/UIScrollView+SPRefresh.swift | 64 +++++++++ ShortPlay/Base/Networking/API/SPHomeAPI.swift | 14 ++ ShortPlay/Base/Networking/API/SPUserAPI.swift | 23 ++++ .../Base/Networking/API/SPVideoAPI.swift | 14 ++ ShortPlay/Base/Networking/Base/SPApi.swift | 2 +- .../Base/Networking/Base/SPNetwork.swift | 58 ++++----- .../Base/Networking/Base/SPURLPath.swift | 5 + .../Controller/SPSearchViewController.swift | 66 ++++++++++ .../Home/View/SPSearchAssociativeCell.swift | 75 +++++++++++ .../Home/View/SPSearchAssociativeView.swift | 96 ++++++++++++++ .../Class/Home/View/SPSearchInputView.swift | 6 + .../Class/Home/View/SPSearchResultCell.swift | 20 +++ .../Class/Home/View/SPSearchResultView.swift | 71 ++++++++++ .../Home/ViewModel/SPSearchViewModel.swift | 14 ++ .../Controller/SPMineViewController.swift | 10 +- .../SPCollectListViewController.swift | 121 ++++++++++++++++++ .../Controller/SPMyListViewController.swift | 91 +++++++++++++ .../SPPlayHistoryViewController.swift | 105 +++++++++++++++ .../Class/MyList/View/SPCollectListCell.swift | 66 ++++++++++ .../Class/Player/Model/SPShortModel.swift | 3 + ShortPlay/Libs/User/SPUserInfo.swift | 23 ++++ .../tabbar_icon_04.imageset/Contents.json | 4 +- .../tabbar_icon_04.imageset/Frame@2x.png | Bin 984 -> 0 bytes .../tabbar_icon_04.imageset/Frame@3x.png | Bin 2049 -> 0 bytes .../tabbar_icon_04.imageset/My list@2x.png | Bin 0 -> 872 bytes .../tabbar_icon_04.imageset/My list@3x.png | Bin 0 -> 1511 bytes .../Contents.json | 2 + .../My list@2x.png | Bin 0 -> 1856 bytes .../My list@3x.png | Bin 0 -> 3270 bytes .../icon/play_icon_03.imageset/Contents.json | 22 ++++ .../icon/play_icon_03.imageset/play@2x.png | Bin 0 -> 895 bytes .../icon/play_icon_03.imageset/play@3x.png | Bin 0 -> 1534 bytes ShortPlay/Source/ShortPlay-Bridging-Header.h | 1 + ShortPlay/Source/en.lproj/Localizable.strings | 3 + 37 files changed, 957 insertions(+), 38 deletions(-) create mode 100644 ShortPlay/Base/Extension/UIScrollView+SPRefresh.swift create mode 100644 ShortPlay/Base/Networking/API/SPUserAPI.swift create mode 100644 ShortPlay/Class/Home/View/SPSearchAssociativeCell.swift create mode 100644 ShortPlay/Class/Home/View/SPSearchAssociativeView.swift create mode 100644 ShortPlay/Class/Home/View/SPSearchResultCell.swift create mode 100644 ShortPlay/Class/Home/View/SPSearchResultView.swift create mode 100644 ShortPlay/Class/Home/ViewModel/SPSearchViewModel.swift create mode 100644 ShortPlay/Class/MyList/Controller/SPCollectListViewController.swift create mode 100644 ShortPlay/Class/MyList/Controller/SPMyListViewController.swift create mode 100644 ShortPlay/Class/MyList/Controller/SPPlayHistoryViewController.swift create mode 100644 ShortPlay/Class/MyList/View/SPCollectListCell.swift delete mode 100644 ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Frame@2x.png delete mode 100644 ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Frame@3x.png create mode 100644 ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/My list@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/My list@3x.png create mode 100644 ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/My list@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/My list@3x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/play_icon_03.imageset/Contents.json create mode 100644 ShortPlay/Source/Assets.xcassets/icon/play_icon_03.imageset/play@2x.png create mode 100644 ShortPlay/Source/Assets.xcassets/icon/play_icon_03.imageset/play@3x.png diff --git a/ShortPlay/Base/Controller/SPTabBarController.swift b/ShortPlay/Base/Controller/SPTabBarController.swift index b3eacf2..032dcfb 100644 --- a/ShortPlay/Base/Controller/SPTabBarController.swift +++ b/ShortPlay/Base/Controller/SPTabBarController.swift @@ -15,10 +15,12 @@ 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: SPExploreViewController(), title: "For You".localized, image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected")) + + let nav4 = createNavigationController(viewController: SPMyListViewController(), title: "My list".localized, image: UIImage(named: "tabbar_icon_04"), selectedImage: UIImage(named: "tabbar_icon_04_selected")) let nav5 = createNavigationController(viewController: SPMineViewController(), title: "Profile".localized, image: UIImage(named: "tabbar_icon_05"), selectedImage: UIImage(named: "tabbar_icon_05_selected")) - self.viewControllers = [nav1, nav2, nav5] + self.viewControllers = [nav1, nav2, nav4, nav5] } diff --git a/ShortPlay/Base/Controller/SPViewController.swift b/ShortPlay/Base/Controller/SPViewController.swift index 7c10fc3..4123e77 100644 --- a/ShortPlay/Base/Controller/SPViewController.swift +++ b/ShortPlay/Base/Controller/SPViewController.swift @@ -90,6 +90,10 @@ class SPViewController: UIViewController, JYPageChildContollerProtocol { func fetchChildControllerScrollView() -> UIScrollView? { return nil } + + func handleHeaderRefresh(_ completer: (() -> Void)?) {} + + func handleFooterRefresh(_ completer: (() -> Void)?) {} } diff --git a/ShortPlay/Base/Extension/UIColor+SPAdd.swift b/ShortPlay/Base/Extension/UIColor+SPAdd.swift index 9bc54c8..742cf56 100644 --- a/ShortPlay/Base/Extension/UIColor+SPAdd.swift +++ b/ShortPlay/Base/Extension/UIColor+SPAdd.swift @@ -76,5 +76,13 @@ extension UIColor { static func colorD568D2(alpha: CGFloat = 1) -> UIColor { return color(hex: 0xD568D2, alpha: alpha) } + + static func color8A899F(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0x8A899F, alpha: alpha) + } + + static func color888888(alpha: CGFloat = 1) -> UIColor { + return color(hex: 0x888888, alpha: alpha) + } } diff --git a/ShortPlay/Base/Extension/UIScrollView+SPRefresh.swift b/ShortPlay/Base/Extension/UIScrollView+SPRefresh.swift new file mode 100644 index 0000000..c58efb3 --- /dev/null +++ b/ShortPlay/Base/Extension/UIScrollView+SPRefresh.swift @@ -0,0 +1,64 @@ +// +// UIScrollView+SPRefresh.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/19. +// + +import UIKit + +extension UIScrollView { + func sp_addRefreshHeader(insetTop: CGFloat = 0, block: (() -> Void)?) { + + + self.mj_header = MJRefreshNormalHeader(refreshingBlock: { + block?() + }) + self.mj_header?.ignoredScrollViewContentInsetTop = insetTop + } + + func sp_addRefreshFooter(insetBottom: CGFloat = 0, block: (() -> Void)?) { + let footer = MJRefreshAutoNormalFooter(refreshingBlock: { + block?() + }) + footer.ignoredScrollViewContentInsetBottom = insetBottom +// footer.stateLabel?.font = .text_sm +// footer.stateLabel?.textColor = .system_text_secondary_300 + + self.mj_footer = footer + + + } + +// func sp_setRefreshFooterTitle(title: String = NSLocalizedString("已经到底了~", comment: ""), state: MJRefreshState) { +// (self.mj_footer as? MJRefreshAutoStateFooter)?.setTitle(title, for: state) +// } + + func sp_addRefreshBackFooter(insetBottom: CGFloat = 0, block: (() -> Void)?) { + self.mj_footer = MJRefreshBackNormalFooter(refreshingBlock: { + block?() + }) + + self.mj_footer?.ignoredScrollViewContentInsetBottom = insetBottom + } + + func sp_endHeaderRefreshing() { + self.mj_header?.endRefreshing() + } + + func sp_endFooterRefreshing() { + if self.mj_footer?.state == .noMoreData { return } + self.mj_footer?.endRefreshing() + } + + ///重置没有更多 + func sp_resetNoMoreData() { + self.mj_footer?.resetNoMoreData() + } + + func sp_endRefreshingWithNoMoreData() { +// self.mj_footer?.state = .noMoreData + self.mj_footer?.endRefreshingWithNoMoreData() + } + +} diff --git a/ShortPlay/Base/Networking/API/SPHomeAPI.swift b/ShortPlay/Base/Networking/API/SPHomeAPI.swift index e29989b..4e242fa 100644 --- a/ShortPlay/Base/Networking/API/SPHomeAPI.swift +++ b/ShortPlay/Base/Networking/API/SPHomeAPI.swift @@ -46,5 +46,19 @@ class SPHomeAPI: NSObject { } + ///搜索 + static func requestSearch(text: String, completer: ((_ list: [SPShortModel]?) -> Void)?) { + var param = SPNetworkParameters(path: "/search") + param.method = .get + param.parameters = [ + "search" : text + ] + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse>) in + completer?(response.data?.list) + } + + } + } diff --git a/ShortPlay/Base/Networking/API/SPUserAPI.swift b/ShortPlay/Base/Networking/API/SPUserAPI.swift new file mode 100644 index 0000000..2df64a9 --- /dev/null +++ b/ShortPlay/Base/Networking/API/SPUserAPI.swift @@ -0,0 +1,23 @@ +// +// SPUserAPI.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/18. +// + +import UIKit + +class SPUserAPI: NSObject { + + + static func requestUserInfo(completer: ((_ userInfo: SPUserInfo?) -> Void)?) { + + var param = SPNetworkParameters(path: "/customer/info") + param.method = .get + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse) in + completer?(response.data) + } + } + +} diff --git a/ShortPlay/Base/Networking/API/SPVideoAPI.swift b/ShortPlay/Base/Networking/API/SPVideoAPI.swift index 46a6633..e429e95 100644 --- a/ShortPlay/Base/Networking/API/SPVideoAPI.swift +++ b/ShortPlay/Base/Networking/API/SPVideoAPI.swift @@ -69,6 +69,20 @@ class SPVideoAPI: NSObject { } } + ///收藏列表 + static func requestCollectList(page: Int, completer: ((_ listModel: SPListModel?) -> Void)?) { + var param = SPNetworkParameters(path: "/myCollections") + param.method = .get + param.parameters = [ + "current_page" : page, + "page_size" : 20 + ] + + SPNetwork.request(parameters: param) { (response: SPNetworkResponse>) in + completer?(response.data) + } + } + } extension SPVideoAPI { diff --git a/ShortPlay/Base/Networking/Base/SPApi.swift b/ShortPlay/Base/Networking/Base/SPApi.swift index dcd77b8..4c40130 100644 --- a/ShortPlay/Base/Networking/Base/SPApi.swift +++ b/ShortPlay/Base/Networking/Base/SPApi.swift @@ -51,7 +51,7 @@ extension SPApi: TargetType { var path: String { switch self { case .request(let parameters): - return parameters.path + return SPURLPathPrefix + parameters.path default: return "" } diff --git a/ShortPlay/Base/Networking/Base/SPNetwork.swift b/ShortPlay/Base/Networking/Base/SPNetwork.swift index 25ed568..d39c076 100644 --- a/ShortPlay/Base/Networking/Base/SPNetwork.swift +++ b/ShortPlay/Base/Networking/Base/SPNetwork.swift @@ -72,44 +72,36 @@ class SPNetwork: NSObject { let code = response.statusCode if code == 401 || code == 402 || code == 403 { - ///重新获取token - self.requestToken(completer: nil) - - ///将请求失败数据重新请求 - if let tokenOperation = self.tokenOperation, parameters.path != "/customer/register" { - - let requestOperation = BlockOperation { - let semaphore = DispatchSemaphore(value: 0) - _request(parameters: parameters) { (response: SPNetworkResponse) in - semaphore.signal() - completion?(response) - } - semaphore.wait() + if parameters.path == "/customer/register" { + var res = SPNetworkResponse() + res.code = -1 + if parameters.isToast { + SPToast.show(text: "Error".localized) } - ///设置依赖关系 - requestOperation.addDependency(tokenOperation) + completion?(res) + } else { + ///重新获取token + self.requestToken(completer: nil) - operationQueue.addOperation(requestOperation) + ///将请求失败数据重新请求 + if let tokenOperation = self.tokenOperation, parameters.path != "/customer/register" { + + let requestOperation = BlockOperation { + let semaphore = DispatchSemaphore(value: 0) + _request(parameters: parameters) { (response: SPNetworkResponse) in + semaphore.signal() + completion?(response) + } + semaphore.wait() + } + ///设置依赖关系 + requestOperation.addDependency(tokenOperation) + + operationQueue.addOperation(requestOperation) + } } - -// if !SPLoginManager.manager.isRefreshingToken { -// SPLoginManager.manager.requestVisitorLogin { -// if let _ = SPLoginManager.manager.token { -// self.request(parameters: parameters, completion: completion) -// } -// } -// } else { -// if parameters.path != "/customer/register" { -//// while SPLoginManager.manager.isRefreshingToken { -//// RunLoop.current.run(mode: .default, before: Date.distantFuture) -//// } -//// if let _ = SPLoginManager.manager.token { -//// self.request(parameters: parameters, completion: completion) -//// } -// } -// } return } diff --git a/ShortPlay/Base/Networking/Base/SPURLPath.swift b/ShortPlay/Base/Networking/Base/SPURLPath.swift index d83137a..fea8279 100644 --- a/ShortPlay/Base/Networking/Base/SPURLPath.swift +++ b/ShortPlay/Base/Networking/Base/SPURLPath.swift @@ -21,9 +21,14 @@ import UIKit #if DEBUG let SPBaseURL = "https://test1-api.guyantv.com" +let SPURLPathPrefix = "" +//let SPBaseURL = "https://api-mireotv.mireotv.com" +//let SPURLPathPrefix = "/4da6fd4c" + let SPWebBaseURL = "https://www.guyantv.com" #else let SPBaseURL = "https://test1-api.guyantv.com" +let SPURLPathPrefix = "/4da6fd4c" let SPWebBaseURL = "https://www.guyantv.com" #endif diff --git a/ShortPlay/Class/Home/Controller/SPSearchViewController.swift b/ShortPlay/Class/Home/Controller/SPSearchViewController.swift index 0454167..6f28531 100644 --- a/ShortPlay/Class/Home/Controller/SPSearchViewController.swift +++ b/ShortPlay/Class/Home/Controller/SPSearchViewController.swift @@ -19,6 +19,29 @@ class SPSearchViewController: SPViewController { private lazy var searchInputView: SPSearchInputView = { let view = SPSearchInputView() view.textField.delegate = self + view.textDidChange = { [weak self] text in + self?.textDidChange(text: text) + } + return view + }() + + ///搜索首页 + private lazy var homeView: SPSearchHomeView = { + let view = SPSearchHomeView() + return view + }() + + ///联想页面 + private lazy var associativeView: SPSearchAssociativeView = { + let view = SPSearchAssociativeView() + view.isHidden = true + return view + }() + + ///搜索结果 + private lazy var resultView: SPSearchResultView = { + let view = SPSearchResultView() + view.isHidden = true return view }() @@ -41,6 +64,9 @@ extension SPSearchViewController { private func _setupUI() { view.addSubview(backButton) view.addSubview(searchInputView) + view.addSubview(homeView) + view.addSubview(resultView) + view.addSubview(associativeView) backButton.snp.makeConstraints { make in make.left.equalToSuperview().offset(5) @@ -54,6 +80,19 @@ extension SPSearchViewController { make.left.equalTo(backButton.snp.right).offset(5) } + homeView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalTo(searchInputView.snp.bottom).offset(10) + } + + resultView.snp.makeConstraints { make in + make.edges.equalTo(homeView) + } + + associativeView.snp.makeConstraints { make in + make.edges.equalTo(homeView) + } + } } @@ -69,4 +108,31 @@ extension SPSearchViewController: UITextFieldDelegate { spLog(message: "结束编辑") } + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + spLog(message: "点击搜索") + if let text = textField.text, text.count > 0 { +// self.requestSearch(text: text) + } + + return true + } + ///文本发生变化 + func textDidChange(text: String) { + if text.count > 0 { + self.associativeView.isHidden = false + self.homeView.isHidden = true + } else { + self.associativeView.isHidden = true + self.homeView.isHidden = false + } + self.associativeView.search(text: text) + } + + + +} + +extension SPSearchViewController { + + } diff --git a/ShortPlay/Class/Home/View/SPSearchAssociativeCell.swift b/ShortPlay/Class/Home/View/SPSearchAssociativeCell.swift new file mode 100644 index 0000000..907e59e --- /dev/null +++ b/ShortPlay/Class/Home/View/SPSearchAssociativeCell.swift @@ -0,0 +1,75 @@ +// +// SPSearchAssociativeCell.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/18. +// + +import UIKit + +class SPSearchAssociativeCell: SPTableViewCell { + + + var searchText: String? + + var model: SPShortModel? { + didSet { + + if let string = model?.titleAttributedString { + titleLabel.attributedText = string + } else { + let string = NSMutableAttributedString(string: model?.name ?? "") + string.color = .color8A899F() + + if let range = model?.name?.ocString().range(of: searchText ?? "") { + string.setColor(.colorFFFFFF(), range: range) + } + + titleLabel.attributedText = string + } + + } + } + + private lazy var iconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "play_icon_03")) + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 14) + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + _setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SPSearchAssociativeCell { + + private func _setupUI() { + contentView.addSubview(iconImageView) + contentView.addSubview(titleLabel) + + iconImageView.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(15) + } + + titleLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.equalToSuperview().offset(40) + make.right.lessThanOrEqualToSuperview().offset(-15) + } + } + +} diff --git a/ShortPlay/Class/Home/View/SPSearchAssociativeView.swift b/ShortPlay/Class/Home/View/SPSearchAssociativeView.swift new file mode 100644 index 0000000..e455b5d --- /dev/null +++ b/ShortPlay/Class/Home/View/SPSearchAssociativeView.swift @@ -0,0 +1,96 @@ +// +// SPSearchAssociativeView.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/18. +// + +import UIKit + +class SPSearchAssociativeView: UIView { + + + private(set) lazy var searchText: String = "" + + private lazy var dataArr: [SPShortModel] = [] + + private lazy var tableView: SPTableView = { + let tableView = SPTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + tableView.rowHeight = 50 + tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: kSPTabbarSafeBottomMargin, right: 0) + SPSearchAssociativeCell.registerCell(tableView: tableView) + return tableView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + _setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func search(text: String) { + self.searchText = text + + self.requestSearch(text: text) + + } +} + +extension SPSearchAssociativeView { + + private func _setupUI() { + addSubview(tableView) + + tableView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + } + +} + +//MARK: -------------- UITableViewDelegate & UITableViewDataSource -------------- +extension SPSearchAssociativeView: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = SPSearchAssociativeCell.dequeueReusableCell(tableView: tableView, indexPath: indexPath) + cell.searchText = self.searchText + cell.model = dataArr[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dataArr.count + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let model = self.dataArr[indexPath.row] + + let vc = SPPlayerDetailViewController() + vc.shortPlayId = model.short_play_id + self.viewController?.navigationController?.pushViewController(vc, animated: true) + } +} + +extension SPSearchAssociativeView { + + private func requestSearch(text: String) { + + SPHomeAPI.requestSearch(text: text) { [weak self] list in + guard let self = self else { return } + if self.searchText != text { return; } + + if let list = list { + self.dataArr = list + self.tableView.reloadData() + } + } + + } +} diff --git a/ShortPlay/Class/Home/View/SPSearchInputView.swift b/ShortPlay/Class/Home/View/SPSearchInputView.swift index 73983f5..4c8ffdc 100644 --- a/ShortPlay/Class/Home/View/SPSearchInputView.swift +++ b/ShortPlay/Class/Home/View/SPSearchInputView.swift @@ -19,6 +19,8 @@ class SPSearchInputView: UIView { } } + var textDidChange: ((_ text: String) -> Void)? + //MARK: UI属性 private lazy var iconImageView: UIImageView = { let imageView = UIImageView(image: UIImage(named: "search_icon_02")) @@ -30,6 +32,10 @@ class SPSearchInputView: UIView { private(set) lazy var textField: SPTextField = { let textField = SPTextField() textField.font = .fontRegular(ofSize: 12) + textField.returnKeyType = .search + textField.textDidChange = { [weak self] text in + self?.textDidChange?(text) + } return textField }() diff --git a/ShortPlay/Class/Home/View/SPSearchResultCell.swift b/ShortPlay/Class/Home/View/SPSearchResultCell.swift new file mode 100644 index 0000000..74f2e2e --- /dev/null +++ b/ShortPlay/Class/Home/View/SPSearchResultCell.swift @@ -0,0 +1,20 @@ +// +// SPSearchResultCell.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/18. +// + +import UIKit + +class SPSearchResultCell: SPTableViewCell { + + /* + // Only override draw() if you perform custom drawing. + // An empty implementation adversely affects performance during animation. + override func draw(_ rect: CGRect) { + // Drawing code + } + */ + +} diff --git a/ShortPlay/Class/Home/View/SPSearchResultView.swift b/ShortPlay/Class/Home/View/SPSearchResultView.swift new file mode 100644 index 0000000..2514408 --- /dev/null +++ b/ShortPlay/Class/Home/View/SPSearchResultView.swift @@ -0,0 +1,71 @@ +// +// SPSearchResultView.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/18. +// + +import UIKit + +class SPSearchResultView: UIView { + + + private lazy var tableView: SPTableView = { + let tableView = SPTableView(frame: .zero, style: .plain) + tableView.delegate = self + tableView.dataSource = self + SPSearchResultCell.registerCell(tableView: tableView) + return tableView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + +// _setupUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SPSearchResultView { + + private func _setupUI() { + addSubview(tableView) + + tableView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + + + } + +} + +//MARK: -------------- UITableViewDelegate & UITableViewDataSource -------------- +extension SPSearchResultView: UITableViewDelegate, UITableViewDataSource { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 10 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = SPSearchResultCell.dequeueReusableCell(tableView: tableView, indexPath: indexPath) + + return cell + } + + + +} + +extension SPSearchResultView { + + private func requestSearch(text: String) { + +// SPHomeAPI.requestSearch(text: text) + + } +} diff --git a/ShortPlay/Class/Home/ViewModel/SPSearchViewModel.swift b/ShortPlay/Class/Home/ViewModel/SPSearchViewModel.swift new file mode 100644 index 0000000..3ae6662 --- /dev/null +++ b/ShortPlay/Class/Home/ViewModel/SPSearchViewModel.swift @@ -0,0 +1,14 @@ +// +// SPSearchViewModel.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/18. +// + +import UIKit + +class SPSearchViewModel: NSObject { + + + +} diff --git a/ShortPlay/Class/Mine/Controller/SPMineViewController.swift b/ShortPlay/Class/Mine/Controller/SPMineViewController.swift index a4ad4f5..8faf1f5 100644 --- a/ShortPlay/Class/Mine/Controller/SPMineViewController.swift +++ b/ShortPlay/Class/Mine/Controller/SPMineViewController.swift @@ -33,6 +33,8 @@ class SPMineViewController: SPViewController { override func viewDidLoad() { super.viewDidLoad() + requestUserInfo() + _setupUI() } @@ -90,6 +92,12 @@ extension SPMineViewController: UITableViewDelegate, UITableViewDataSource { extension SPMineViewController { -// func + private func requestUserInfo() { + + SPUserAPI.requestUserInfo { userInfo in + + + } + } } diff --git a/ShortPlay/Class/MyList/Controller/SPCollectListViewController.swift b/ShortPlay/Class/MyList/Controller/SPCollectListViewController.swift new file mode 100644 index 0000000..9f7e52b --- /dev/null +++ b/ShortPlay/Class/MyList/Controller/SPCollectListViewController.swift @@ -0,0 +1,121 @@ +// +// SPCollectListViewController.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/18. +// + +import UIKit + +class SPCollectListViewController: SPViewController { + + + private lazy var dataArr: [SPShortModel] = [] + private var page: Int? + + //MARK: UI属性 + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let itemWidth = floor((kSPScreenWidth - 30 - 9 * 2) / 3) + let itemHeight = 146 / 109 * itemWidth + 36 + + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: itemWidth, height: itemHeight) + layout.minimumLineSpacing = 10 + layout.minimumInteritemSpacing = 9 + 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.contentInset = .init(top: 10, left: 0, bottom: 0, right: 0) + collectionView.sp_addRefreshHeader(insetTop: collectionView.contentInset.top) { [weak self] in + self?.handleHeaderRefresh(nil) + } + collectionView.sp_addRefreshFooter { [weak self] in + self?.handleFooterRefresh(nil) + } + SPCollectListCell.registerCell(collectionView: collectionView) + return collectionView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + + requestDataList(page: 1, completer: nil) + + _setupUI() + } + + override func setBgImageView() { } + + + override func handleHeaderRefresh() { + requestDataList(page: 1) { [weak self] in + self?.collectionView.sp_endHeaderRefreshing() + } + } + + + +} + +extension SPCollectListViewController { + + private func _setupUI() { + view.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(10) + } + } + +} + +//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource -------------- +extension SPCollectListViewController: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = SPCollectListCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath) + cell.model = dataArr[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.dataArr.count + } + +} + +extension SPCollectListViewController { + + private func requestDataList(page: Int, completer: (() -> Void)?) { + + SPVideoAPI.requestCollectList(page: page) { [weak self] listModel in + guard let self = self else { return } + + if let listModel = listModel, let list = listModel.list { + if page == 1 { + self.dataArr.removeAll() + } + self.dataArr += list + + self.page = page + + self.collectionView.reloadData() + + } + completer?() + } + + } + +} + + + diff --git a/ShortPlay/Class/MyList/Controller/SPMyListViewController.swift b/ShortPlay/Class/MyList/Controller/SPMyListViewController.swift new file mode 100644 index 0000000..2fb20ed --- /dev/null +++ b/ShortPlay/Class/MyList/Controller/SPMyListViewController.swift @@ -0,0 +1,91 @@ +// +// SPMyListViewController.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/18. +// + +import UIKit + +class SPMyListViewController: SPViewController { + + + private lazy var titles = ["Follow List".localized, "Play List".localized] + + private lazy var viewControllers: [SPViewController] = { + let vc1 = SPCollectListViewController() + let vc2 = SPPlayHistoryViewController() + return [vc1, vc2] + }() + + private lazy var pageView: JYPageController = { + let pageView = JYPageController() + pageView.delegate = self + pageView.dataSource = self + pageView.config.indicatorWidth = 20 + pageView.config.indicatorHeight = 4 + pageView.config.indicatorCornerRadius = 2 + pageView.config.indicatorColor = .colorFFFFFF(alpha: 0.9) + pageView.config.selectedTitleColor = .colorFFFFFF(alpha: 0.9) + pageView.config.selectedTitleFont = 16 + pageView.config.selectedTitleFontWeight = .medium + pageView.config.normalTitleColor = .color888888() + pageView.config.normalTitleFont = 15 + pageView.config.normalTitleFontWeight = .regular + pageView.config.leftPadding = 15 + pageView.config.itemsMargin = 40 + return pageView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + _setupUI() + } + + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.navigationController?.setNavigationBarHidden(true, animated: true) + } + + + +} + +extension SPMyListViewController { + private func _setupUI() { + addChild(pageView) + view.addSubview(pageView.view) + + pageView.view.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } +} + + +//MARK: -------------- JYPageControllerDelegate & JYPageControllerDataSource -------------- +extension SPMyListViewController: JYPageControllerDelegate, JYPageControllerDataSource { + func pageController(_ pageController: JYPageController, frameForSegmentedView segmentedView: JYSegmentedView) -> CGRect { + return .init(x: 0, y: kSPStatusbarHeight + 10, width: kSPScreenWidth, height: 35) + } + + func pageController(_ pageController: JYPageController, frameForContainerView container: UIScrollView) -> CGRect { + let y = kSPStatusbarHeight + 10 + 35 + return .init(x: 0, y: kSPStatusbarHeight + 10 + 35, width: kSPScreenWidth, height: kSPScreenHeight - y - kSPTabBarHeight) + } + + func pageController(_ pageController: JYPageController, titleAt index: Int) -> String { + return titles[index] + } + + func numberOfChildControllers() -> Int { + return titles.count + } + + func childController(atIndex index: Int) -> any JYPageChildContollerProtocol { + return self.viewControllers[index] + } + +} diff --git a/ShortPlay/Class/MyList/Controller/SPPlayHistoryViewController.swift b/ShortPlay/Class/MyList/Controller/SPPlayHistoryViewController.swift new file mode 100644 index 0000000..91047f2 --- /dev/null +++ b/ShortPlay/Class/MyList/Controller/SPPlayHistoryViewController.swift @@ -0,0 +1,105 @@ +// +// SPPlayHistoryViewController.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/19. +// + +import UIKit + +class SPPlayHistoryViewController: SPViewController { + + private lazy var dataArr: [SPShortModel] = [] + private var page: Int? + + //MARK: UI属性 + private lazy var collectionViewLayout: UICollectionViewFlowLayout = { + let itemWidth = floor((kSPScreenWidth - 30 - 9 * 2) / 3) + let itemHeight = 146 / 109 * itemWidth + 36 + + let layout = UICollectionViewFlowLayout() + layout.itemSize = .init(width: itemWidth, height: itemHeight) + layout.minimumLineSpacing = 10 + layout.minimumInteritemSpacing = 9 + layout.sectionInset = .init(top: 10, 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 + SPCollectListCell.registerCell(collectionView: collectionView) + return collectionView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + + requestDataList(page: 1, completer: nil) + + _setupUI() + } + + override func setBgImageView() { } + + + + +} + +extension SPPlayHistoryViewController { + + private func _setupUI() { + view.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.top.equalToSuperview().offset(10) + } + } + +} + +//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource -------------- +extension SPPlayHistoryViewController: UICollectionViewDelegate, UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = SPCollectListCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath) + cell.model = dataArr[indexPath.row] + return cell + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.dataArr.count + } + +} + +extension SPPlayHistoryViewController { + + private func requestDataList(page: Int, completer: (() -> Void)?) { + + SPVideoAPI.requestCollectList(page: page) { [weak self] listModel in + guard let self = self else { return } + + if let listModel = listModel, let list = listModel.list { + if page == 1 { + self.dataArr.removeAll() + } + self.dataArr += list + + self.page = page + + self.collectionView.reloadData() + + } + completer?() + } + + } + +} + diff --git a/ShortPlay/Class/MyList/View/SPCollectListCell.swift b/ShortPlay/Class/MyList/View/SPCollectListCell.swift new file mode 100644 index 0000000..10e3160 --- /dev/null +++ b/ShortPlay/Class/MyList/View/SPCollectListCell.swift @@ -0,0 +1,66 @@ +// +// SPCollectListCell.swift +// ShortPlay +// +// Created by 曾觉新 on 2025/4/18. +// + +import UIKit + +class SPCollectListCell: SPCollectionViewCell { + + var model: SPShortModel? { + didSet { + coverImageView.sp_setImage(url: model?.image_url) + titleLabel.text = model?.name + } + } + + private lazy var coverImageView: SPImageView = { + let imageView = SPImageView() + imageView.layer.cornerRadius = 7 + imageView.layer.masksToBounds = true + return imageView + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = .fontRegular(ofSize: 13) + label.textColor = .colorFFFFFF(alpha: 0.9) + label.numberOfLines = 2 + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + + _setupUI() + } + + @MainActor required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + +} + +extension SPCollectListCell { + + private func _setupUI() { + contentView.addSubview(coverImageView) + contentView.addSubview(titleLabel) + + coverImageView.snp.makeConstraints { make in + make.left.right.top.equalToSuperview() + make.bottom.equalToSuperview().offset(-36) + } + + titleLabel.snp.makeConstraints { make in + make.left.equalToSuperview() + make.top.equalTo(coverImageView.snp.bottom).offset(5) + make.right.lessThanOrEqualToSuperview() + } + + } + +} diff --git a/ShortPlay/Class/Player/Model/SPShortModel.swift b/ShortPlay/Class/Player/Model/SPShortModel.swift index c25c34a..2137277 100644 --- a/ShortPlay/Class/Player/Model/SPShortModel.swift +++ b/ShortPlay/Class/Player/Model/SPShortModel.swift @@ -28,6 +28,9 @@ class SPShortModel: SPModel, SmartCodable { var video_info: SPVideoInfoModel? var watch_total: Int? + @IgnoredKey + var titleAttributedString: NSAttributedString? + static func mappingForKey() -> [SmartKeyTransformer]? { return [ diff --git a/ShortPlay/Libs/User/SPUserInfo.swift b/ShortPlay/Libs/User/SPUserInfo.swift index 28b5479..5c0b35f 100644 --- a/ShortPlay/Libs/User/SPUserInfo.swift +++ b/ShortPlay/Libs/User/SPUserInfo.swift @@ -11,6 +11,29 @@ import SmartCodable class SPUserInfo: SPModel, SmartCodable, NSSecureCoding { + var id: String? + var customer_id: String? + var is_guide_vip: String? + var is_tourist: String? + var family_name: String? + var giving_name: String? + var vip_end_time: String? + var third_access_id: String? + var is_vip: Bool? + var coin_left_total: Int? + var vip_type: String? + var email: String? + var third_access_platform: String? + var ip_address: String? + var country_code: String? + var user_level: String? + var send_coin_left_total: String? + var avator: String? + var sign_in_status: String? + var registered_days: String? + var ln: String? + var country: String? + required init() { } diff --git a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Contents.json index 6aa90f6..14066fc 100644 --- a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Contents.json +++ b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Contents.json @@ -5,12 +5,12 @@ "scale" : "1x" }, { - "filename" : "Frame@2x.png", + "filename" : "My list@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "Frame@3x.png", + "filename" : "My list@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Frame@2x.png b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Frame@2x.png deleted file mode 100644 index 24790e265831108ab9ae3c44ce2884669b3b2a7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 984 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 diff --git a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/My list@2x.png b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/My list@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1621f005f5295b98bc38a8e482ef0570e5f13e90 GIT binary patch literal 872 zcmV-u1DE`XP)Px&B1uF+RA@u(S+Q#qVHAJwF0>gs*w(?JgF^-7F5e|Zi$jGL9o)2_w1QH*2@1OS zSGYJhR1mCKsG`LV4hl*OR*)Kdx$k;Vu%LrNhNd_;1e(h;Pp?oyn+un_Na=TDa^LU0 z-|zRn%*pLer$vn zNS;`Ch-wc1VR*04zNJ#kF14_qDDxKPz&P|h?nf6 zM2r1}E?0X%?SarCX?%kkAvHp3gc5$GY*FCQnx>7_-B;wuvRbV^$Y!%Y>q)z=J4;04M0CZntY?v;MccNs zx~|&*P%M|rxxvA~A~)32({lk4x$$#5fZq|(!_Cdj`Ms27r?$4Xu4*>Wmye{=>8EvSvm#Tvu0Jsh zgGofbSnbZv&WUQZdJ7Sc?g08!E|*8y^_B>1FXnOL4N63-xm>Ogx#I>43x&dvrfL1Y y?{n1HREPx)p-DtRRCr$Pn_XyJMHI*XXSOC0tCSd2P|!--q-O8j)J9SxXhVxdr4mc2SRYhu z1+_2r1FM2HU{#8OLaShlt%wzZ&`{BuTKggTVu{^*Z=xI0c2THO*oSnJvQ0KS#~rvW zOHHynx#kOJU>`1f=AJXZ`JclsoI|i{TmoeIj8DJRMD;S$PjLF9C4Pq()o*%P$boNu|^g*L73nZ~M39glf>yqeth|)YL3w=GOtN z1yC6!h)LW#1mG(o+LlhI`&wFBMofJf1JZD+tE*dy=q&)XrVgjcz8AojWHR~nx^?UF zCNUTT3W8uUGw&~gGXUT)fCDCur@;1M04o7p8~RELAy&oX@jp#`8w2t@?+YSYA38Y$ zV3XrGokXNe98b2bu8%7Mu0ren@>c)hc!nl=TRx;K@*2l zZR`8~0{}VzQ~(%a=EWw3+Z50SBH9%ma=+s^YiF8ruDpOyNu6Q3X%%gBU3Yi67i9`) zI}yDczI>CEa%Z`Yrqj;%{Wbt^glYDs>$>{6tYr#lFA+TvzWFIB<=(QqOtX>i`%ePc z69W3obzS{j*0O*~V6uRw15kT=d#tXmZXtjh%hGW)BhKY=f1N&kIyKghz!{ffK@dE` z%pXqGUl1b70(i!8oI|6ro^gOY&wG!EUW_ITv)#=6zU#U#%?d!>-Q9O7rCtLt&+M2J zw(b`~yd96nPs|EHTeof%t*x!s&7C{ttRZuj3wgy=R zSp`KSU=VoZ-?FXP|L)w{nhwHi* z%H2O@fPx^{#mpOYU_|t^<2av=6{PNTU9ez5wGcwjcf1g$QSn?^t}qCr)o_xT_f%C? z9jmRaO^2fci->3qGp`8;QFJR`am%uv6ZuF8@#Wy)V5+I9X@H1$tXO>C-w5E7Fz(%s zRLH+;h=ZNU4PS zgD`!4eU-Uf?xtKWx4h8u`w9Sk0hbpuXNl+>5giyB8v3}gu`zk+%buQ|rhGpCebL-l zqm;6%j*mgMvK zeE_ucvDQT*I;@oXfr!47QeM7Mli3bG2a3W`R+DkvJie*lg@@^*$^ literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Contents.json index d77ddd3..14066fc 100644 --- a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Contents.json +++ b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/Contents.json @@ -5,10 +5,12 @@ "scale" : "1x" }, { + "filename" : "My list@2x.png", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "My list@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/My list@2x.png b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/My list@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0450fbda44d522ff619c7e2e347af9e8a16f0f99 GIT binary patch literal 1856 zcmV-G2fz4Px*`bk7VRA@u(neT5?*BQsZ&#{xZBq2c4Gz=7)&Er>S<_wmR!cZ(Gv>$9vC8}nk zKklV9xiM7To4wgyp#2AXL9J_?;#-X>D{4hmZB;j|a@4_sXpk9@5|}rpC@fhYfRCP!{h7di0sLYhJ^GVi-_LVSotfx5ydF~Q0<&=1MkSJ$vfpHuI|!7n>FN5a zYk+_5F*kXI=Y0awsr1OYVw}J7^eYWsUzzrq^9n&0xH9*7D-S_EJnT&lLk1df-A~Ulsgu$fE#=!u5wbdt50n8a}EKkRS-_;rOl@0AqWP z9s{GpXvB1^|oXKSExWLM*yh#(&^Wf#L95HUJS>D`Pty0Vv1Mb)Wkw80E#NaHnmx z91D4HBfw10HN-4oiM1C6aB}y=V$|gJ5>QpN z63m%p@-J-y*_|v|_s4^70p8QVY9kYtKen|f0OfRY@0HpLZ>7dy#}Le%5tKVxmmm5W zKpJd-ZSDXr87FK77-X%EwG|DZAl`mqVsTZj;)g`!pH~US-v!Gzx0u!A>8}2S1gjL* zSDRbh0bDTpH8GV!Yhs&S0ZcocG_F4)g!sCISS<@O=)bj>7gc9aemx8My;tel-ITd#3?!JLu?^7b z4q(toX~egd(RF6aJp-6Gl>l5YQV9k$hSZzPjcx(XYOt$OSWRYgNdX3pvua#5zTRXu zy8}3D3{F*}ur`=YB?UNd3{EKkz1eJX3vfXL)ILB~v$>|E0B4Ph38+;FtT&tM+ySJF zOBzi91WbQP0nQj{8-T%ji@Dkzz-c3`2@(k~SuJK`NdZ#EdH5v;c8gN^|H zxbLIwB=emBx2pnLdqsRB%YyxN0<(As=tL%$bq7g9nWODpk@X+F+WP8%e2S*4VRE1wc1_Az)`DV1XHutyx+cF_0RTL+tS;Wc~o~RtAqTQ6Dk360maWADT%O#k{;3n`@A}4W8`&bhAfzUSviG z0lukg$iFerN5FTK^8`A+En5Dqxl(f8KRA++$$EnCmjDW*eVz!WZ_Ie3pUz06$V-cam}Aai#i#06sfWMK}P)1`y5%qjY8=PR|Mx;jKX44|vWv9b;Yv uJ`YFlWuNx}?qxLH^mShWH&MHnZTSOBw literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/My list@3x.png b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/My list@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..8c8ca56bac40cbe0865aeb1be6ae2ba0534a9304 GIT binary patch literal 3270 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 Eg7d%}VE_OC literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/Assets.xcassets/icon/play_icon_03.imageset/Contents.json b/ShortPlay/Source/Assets.xcassets/icon/play_icon_03.imageset/Contents.json new file mode 100644 index 0000000..c9f3912 --- /dev/null +++ b/ShortPlay/Source/Assets.xcassets/icon/play_icon_03.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_03.imageset/play@2x.png b/ShortPlay/Source/Assets.xcassets/icon/play_icon_03.imageset/play@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2903cfb224c139c5989aa539b5b21b1333459214 GIT binary patch literal 895 zcmV-_1AzRAP)Px&IY~r8R9HvNm`hJnQ4oM|98q3!|=7Vq9R*7&kt6*ay%Oz>b-ld*uRsoYNPbs^ z{m-DQ0FXw+wS0;>!HCEK0B*xOrNHDG%V0~n0JDM-Q6m7;c>sV9Y9Wb6C;H;CWF(ieO;z8v zds|1t;WHnFCd{t1%3!Skz$J{36aq6Q$=DtCiOx>n?O^cO521;-Dy=b?)Q%Jc)6F1a z+EwR_o<7?h@%g+d3BU~oJ0MeMY1()w5nsA5VT1wayoU}B_g@aa6C9V329xoa6+%&z z=^G$P@$TSLS}y)rRTYv2mU7hb-)am#8G%!oCqGP?}<$~r~%7JPx)xJg7oRA@uxn_o;@RUF5E=W^*k)^_Fp7y^S~1Y{aVhHRQ_E@W}~0EU+_eKPgU z2VabjOEf_r^mQ-u!I(&7%q^Hr7bQ!U&5cc|kqm-smJpqZW|X->2ejN{zTLUa^{$29 zp4+Z;zvQM+?)mdMzu)1Avx!OgXZwL;rqEDN>eAa z3yngvfKDsxc3POGSIoCO_TN%dn7jAZGvTz5oO2fu`Gse+)aqk+8J_yzbf76AHyNNl zrfESRf#jTHS=d_OCYsJzV7hnBbM$|Z!i&O0u}o}Mc_2(T89%NHG`7MJ!rC{gmnoqM?Wp z61xUKVwX@X0#6HV#&u1W5fLJr&FpZ@%7FtyBot-BRDB{omDb|S%&jfc)3=&=TvNKb zcg-|4)h6b}V-f`M_W=nLg~LG*ASGjC(gC?uNOY7jHec#czpJVWjP~~*_{HsZV-DFZOMtAf!?R`s zDL|3a{Kw%`&+h4d{?qR6js=@39{{pSWZtCe&KA7ohuveu79FnAuYmwGx*!=eGmG2A=zIH{zIy69T zlc*qBG$)WmOXUII`)>!|I4#jfZkxI00usCX?e*2cBS&7Hkbq2Rvf7T!oXvAm!-m)8 zbc_tX**8>PF13Cp=A=zah&|qwF}m+?IL=m7`bLf(ef_G02KAx|?`li($_{f?Rps)w zx|*TR&YiOz9WB@k+98Q`5_?IcBBdm2NJcS*DRVx5+3|saS4IN?>2=Y}u?$EglW}S# zC0RgfilT(w9_KeLEe)p+9qRc*ssrf@C(<7{b3}eeN~}ThxYOl4(ca$p#lC$nU?ZK2 zz+C4^q(5xiPyRZ2b4263DcxuxiYZaq#E#~MkNW!d&q|GI=7kq*%M4t)5URd5IraJ4 znzlPd`zxxdUh;d(Mtfd*ZoI9nc`@@oO1+uP4A_$6i9{mm@bC}sMwg=R5iwkrNQk1a z+y1S-q0Y{yFXUhwwb2M9b3C?X>%!r%=iIq#AH-JU2N7XAOV#fW_P+R89yMo%PG;+n zP_`}aidI6Q8-c~e6}79YZ9dQ0IWy$Lyeq!toELWnT|hn&XCcsNv7KryGsY76q9^Bk zQlG7M?MOEA>v{M)?%~br?G)pk0i3nq&Oq`sE!FK0B%7soC3(b>r9}~Xd#B^w+;=sA0uBxd8HD6ED$jGy*c{GBuqU07*qoM6N<$f-f`b0ssI2 literal 0 HcmV?d00001 diff --git a/ShortPlay/Source/ShortPlay-Bridging-Header.h b/ShortPlay/Source/ShortPlay-Bridging-Header.h index e70f190..69e44e7 100644 --- a/ShortPlay/Source/ShortPlay-Bridging-Header.h +++ b/ShortPlay/Source/ShortPlay-Bridging-Header.h @@ -12,3 +12,4 @@ #import "NSUserDefaults+JXAdd.h" #import #import +#import diff --git a/ShortPlay/Source/en.lproj/Localizable.strings b/ShortPlay/Source/en.lproj/Localizable.strings index 7b3ed2e..0a10dde 100644 --- a/ShortPlay/Source/en.lproj/Localizable.strings +++ b/ShortPlay/Source/en.lproj/Localizable.strings @@ -27,6 +27,9 @@ "User Agreement" = "User Agreement"; "Help Center" = "Help Center"; "About Us" = "About Us"; +"My list" = "My list"; +"Follow List" = "Follow List"; +"Play List" = "Play List"; ///视频详情标题 "kPlayerDetailTitleString" = "Episode %@ / %@";