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 24790e2..0000000 Binary files a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Frame@2x.png and /dev/null differ diff --git a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Frame@3x.png b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Frame@3x.png deleted file mode 100644 index fbac8b6..0000000 Binary files a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/Frame@3x.png and /dev/null differ 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 0000000..1621f00 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/My list@2x.png differ diff --git a/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/My list@3x.png b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/My list@3x.png new file mode 100644 index 0000000..883a1b8 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04.imageset/My list@3x.png differ 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 0000000..0450fbd Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/My list@2x.png differ 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 0000000..8c8ca56 Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/TabBar/tabbar_icon_04_selected.imageset/My list@3x.png differ 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 0000000..2903cfb Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/play_icon_03.imageset/play@2x.png differ diff --git a/ShortPlay/Source/Assets.xcassets/icon/play_icon_03.imageset/play@3x.png b/ShortPlay/Source/Assets.xcassets/icon/play_icon_03.imageset/play@3x.png new file mode 100644 index 0000000..bd1931a Binary files /dev/null and b/ShortPlay/Source/Assets.xcassets/icon/play_icon_03.imageset/play@3x.png differ 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 %@ / %@";