收藏列表,播放记录列表

This commit is contained in:
曾觉新 2025-04-19 09:20:15 +08:00
parent b4c526f019
commit 7ff597e1ee
37 changed files with 957 additions and 38 deletions

View File

@ -16,9 +16,11 @@ class SPTabBarController: UITabBarController {
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]
}

View File

@ -91,6 +91,10 @@ class SPViewController: UIViewController, JYPageChildContollerProtocol {
return nil
}
func handleHeaderRefresh(_ completer: (() -> Void)?) {}
func handleFooterRefresh(_ completer: (() -> Void)?) {}
}
extension UIViewController {

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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<SPListModel<SPShortModel>>) in
completer?(response.data?.list)
}
}
}

View File

@ -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<SPUserInfo>) in
completer?(response.data)
}
}
}

View File

@ -69,6 +69,20 @@ class SPVideoAPI: NSObject {
}
}
///
static func requestCollectList(page: Int, completer: ((_ listModel: SPListModel<SPShortModel>?) -> Void)?) {
var param = SPNetworkParameters(path: "/myCollections")
param.method = .get
param.parameters = [
"current_page" : page,
"page_size" : 20
]
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPListModel<SPShortModel>>) in
completer?(response.data)
}
}
}
extension SPVideoAPI {

View File

@ -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 ""
}

View File

@ -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<T>) in
semaphore.signal()
completion?(response)
}
semaphore.wait()
if parameters.path == "/customer/register" {
var res = SPNetworkResponse<T>()
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<T>) 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
}

View File

@ -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

View File

@ -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 {
}

View File

@ -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)
}
}
}

View File

@ -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()
}
}
}
}

View File

@ -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
}()

View File

@ -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
}
*/
}

View File

@ -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)
}
}

View File

@ -0,0 +1,14 @@
//
// SPSearchViewModel.swift
// ShortPlay
//
// Created by on 2025/4/18.
//
import UIKit
class SPSearchViewModel: NSObject {
}

View File

@ -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
}
}
}

View File

@ -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?()
}
}
}

View File

@ -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]
}
}

View File

@ -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?()
}
}
}

View File

@ -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()
}
}
}

View File

@ -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 [

View File

@ -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() { }

View File

@ -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"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 872 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -5,10 +5,12 @@
"scale" : "1x"
},
{
"filename" : "My list@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "My list@3x.png",
"idiom" : "universal",
"scale" : "3x"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -12,3 +12,4 @@
#import "NSUserDefaults+JXAdd.h"
#import <KTVHTTPCache.h>
#import <HWPanModal/HWPanModal.h>
#import <MJRefresh/MJRefresh.h>

View File

@ -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 %@ / %@";