首页开发完成
@ -12,7 +12,7 @@ class SPTabBarController: UITabBarController {
|
|||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
let nav1 = createNavigationController(viewController: SPHomePageController(), title: "Home".localized, image: UIImage(named: "tabbar_icon_01"), selectedImage: UIImage(named: "tabbar_icon_01_selected"))
|
let nav1 = createNavigationController(viewController: SPHomeViewController(), title: "Home".localized, image: UIImage(named: "tabbar_icon_01"), selectedImage: UIImage(named: "tabbar_icon_01_selected"))
|
||||||
|
|
||||||
let nav2 = createNavigationController(viewController: SPExplorePageController(), title: "For You".localized, image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected"))
|
let nav2 = createNavigationController(viewController: SPExplorePageController(), title: "For You".localized, image: UIImage(named: "tabbar_icon_02"), selectedImage: UIImage(named: "tabbar_icon_02_selected"))
|
||||||
|
|
||||||
|
@ -57,6 +57,16 @@ public func spLog(message:Any? , file: String = #file, function: String = #funct
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK:-------------- 计算 --------------
|
||||||
|
///角度转弧度
|
||||||
|
public func kSPAngleToRadians(angle: CGFloat) -> CGFloat {
|
||||||
|
return angle / 180.0 * CGFloat.pi
|
||||||
|
}
|
||||||
|
///弧度转角度
|
||||||
|
public func kSPRadiansToAngle(radians: CGFloat) -> CGFloat {
|
||||||
|
return radians * (180.0 / CGFloat.pi)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public func sp_swizzled_instanceMethod(_ prefix: String, oldClass: Swift.AnyClass!, oldSelector: String, newClass: Swift.AnyClass) {
|
public func sp_swizzled_instanceMethod(_ prefix: String, oldClass: Swift.AnyClass!, oldSelector: String, newClass: Swift.AnyClass) {
|
||||||
let newSelector = prefix + "_" + oldSelector;
|
let newSelector = prefix + "_" + oldSelector;
|
||||||
|
@ -108,5 +108,21 @@ extension UIColor {
|
|||||||
static func color181115(alpha: CGFloat = 1) -> UIColor {
|
static func color181115(alpha: CGFloat = 1) -> UIColor {
|
||||||
return color(hex: 0x181115, alpha: alpha)
|
return color(hex: 0x181115, alpha: alpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func colorFF5100(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return color(hex: 0xFF5100, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorAFAFAF(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return color(hex: 0xAFAFAF, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorA8A5AA(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return color(hex: 0xA8A5AA, alpha: alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func colorD9D9D9(alpha: CGFloat = 1) -> UIColor {
|
||||||
|
return color(hex: 0xD9D9D9, alpha: alpha)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,4 +24,8 @@ extension UIFont {
|
|||||||
static func fontLight(ofSize: CGFloat) -> UIFont {
|
static func fontLight(ofSize: CGFloat) -> UIFont {
|
||||||
return .systemFont(ofSize: ofSize, weight: .light)
|
return .systemFont(ofSize: ofSize, weight: .light)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func fontWeight(ofSize: CGFloat, weight: CGFloat) -> UIFont {
|
||||||
|
return .systemFont(ofSize: ofSize, weight: UIFont.Weight(weight))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,8 @@ class SPHomeAPI: NSObject {
|
|||||||
|
|
||||||
///首页模块接口
|
///首页模块接口
|
||||||
static func requestHomeModuleData(completer: ((_ model: SPHomeModuleModel?) -> Void)?) {
|
static func requestHomeModuleData(completer: ((_ model: SPHomeModuleModel?) -> Void)?) {
|
||||||
var param = SPNetworkParameters(path: "/homeModuleData")
|
let param = SPNetworkParameters(path: "/homeBannerAndNineSquare")
|
||||||
param.method = .get
|
// param.method = .get
|
||||||
|
|
||||||
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPHomeModuleModel>) in
|
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPHomeModuleModel>) in
|
||||||
completer?(response.data)
|
completer?(response.data)
|
||||||
@ -46,6 +46,19 @@ class SPHomeAPI: NSObject {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///首页列表数据
|
||||||
|
static func requestHomeList(page: Int , completer: ((_ listModel: SPListModel<SPShortModel>?) -> Void)?) {
|
||||||
|
var param = SPNetworkParameters(path: "/newShortPlay")
|
||||||
|
param.parameters = [
|
||||||
|
"page_size" : 20,
|
||||||
|
"current_page" : page
|
||||||
|
]
|
||||||
|
|
||||||
|
SPNetwork.request(parameters: param) { (response: SPNetworkResponse<SPListModel<SPShortModel>>) in
|
||||||
|
completer?(response.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///搜索
|
///搜索
|
||||||
static func requestSearch(text: String, completer: ((_ list: [SPShortModel]?) -> Void)?) {
|
static func requestSearch(text: String, completer: ((_ list: [SPShortModel]?) -> Void)?) {
|
||||||
var param = SPNetworkParameters(path: "/search")
|
var param = SPNetworkParameters(path: "/search")
|
||||||
|
@ -8,31 +8,27 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
/*
|
/*
|
||||||
https://api-moviatv.moviatv.com/93f03506/
|
https://admin-thimratv.guyantv.com/login
|
||||||
|
|
||||||
https://api-mireotv.mireotv.com/4da6fd4c/
|
|
||||||
|
|
||||||
https://api-vibeoshort.vibeoshort.com/bf86d973/
|
|
||||||
|
|
||||||
https://api-viontv.viontv.com/b7afef99/
|
|
||||||
|
|
||||||
https://api-zyreotv.zyreotv.com/7834f11d/
|
|
||||||
|
|
||||||
https://api-thimratv.thimratv.com
|
https://api-thimratv.thimratv.com/0a2c5b02/
|
||||||
|
|
||||||
|
新app的web需要适配
|
||||||
|
https://thimratv.com/
|
||||||
|
https://www.thimratv.com/
|
||||||
|
https://campaign.thimratv.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
let SPBaseURL = "https://test1-api.guyantv.com"
|
let SPBaseURL = "https://test1-api.guyantv.com"
|
||||||
//let SPBaseURL = "https://api-thimratv.thimratv.com"
|
|
||||||
let SPURLPathPrefix = ""
|
let SPURLPathPrefix = ""
|
||||||
//let SPBaseURL = "https://api-mireotv.mireotv.com"
|
//let SPBaseURL = "https://api-thimratv.thimratv.com"
|
||||||
//let SPURLPathPrefix = "/4da6fd4c"
|
//let SPURLPathPrefix = "/0a2c5b02"
|
||||||
|
|
||||||
let SPWebBaseURL = "https://www.guyantv.com"
|
let SPWebBaseURL = "https://www.thimratv.com"
|
||||||
#else
|
#else
|
||||||
let SPBaseURL = "https://test1-api.guyantv.com"
|
let SPBaseURL = " https://api-thimratv.thimratv.com"
|
||||||
let SPURLPathPrefix = "/4da6fd4c"
|
let SPURLPathPrefix = "/0a2c5b02"
|
||||||
let SPWebBaseURL = "https://www.guyantv.com"
|
let SPWebBaseURL = "https://www.thimratv.com"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
107
Thimra/Base/View/SPZoomCollectionViewLayout.swift
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
//
|
||||||
|
// SPZoomCollectionViewLayout.swift
|
||||||
|
// Thimra
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPZoomCollectionViewLayout: UICollectionViewFlowLayout {
|
||||||
|
|
||||||
|
let minimumScale: CGFloat = 0.08 // 调整缩放大小
|
||||||
|
|
||||||
|
private(set) var currentIndexPath: IndexPath = IndexPath(row: 0, section: 0)
|
||||||
|
|
||||||
|
|
||||||
|
private var cellWidth: CGFloat {
|
||||||
|
return itemSize.width + minimumLineSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepare() {
|
||||||
|
super.prepare()
|
||||||
|
scrollDirection = .horizontal
|
||||||
|
self.collectionView?.decelerationRate = .fast
|
||||||
|
let screenWidth = UIScreen.main.bounds.size.width
|
||||||
|
let insetLeft = (screenWidth - self.itemSize.width) / 2
|
||||||
|
collectionView?.contentInset = UIEdgeInsets(top: 0, left: insetLeft, bottom: 0, right: insetLeft)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
|
||||||
|
guard let collectionView = collectionView else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) }
|
||||||
|
|
||||||
|
let proposedRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionView.bounds.width, height: collectionView.bounds.height)
|
||||||
|
|
||||||
|
guard let layoutAttributes = layoutAttributesForElements(in: proposedRect) else {
|
||||||
|
return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity)
|
||||||
|
}
|
||||||
|
|
||||||
|
let horizontalCenterX = proposedContentOffset.x + collectionView.bounds.width / 2
|
||||||
|
var offsetAdjustment = CGFloat.greatestFiniteMagnitude
|
||||||
|
// ydLog(message: "offsetAdjustment = \(offsetAdjustment)")
|
||||||
|
// ydLog(message: "horizontalCenterX = \(horizontalCenterX)")
|
||||||
|
|
||||||
|
var currentIndexPath: IndexPath = IndexPath(row: 0, section: 0)
|
||||||
|
for attributes in layoutAttributes {
|
||||||
|
let itemHorizontalCenterX = attributes.center.x
|
||||||
|
let distance = itemHorizontalCenterX - horizontalCenterX
|
||||||
|
// ydLog(message: "distance = \(distance)")
|
||||||
|
//离中心距离最近的
|
||||||
|
if abs(distance) < abs(offsetAdjustment) {
|
||||||
|
offsetAdjustment = distance
|
||||||
|
currentIndexPath = attributes.indexPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.currentIndexPath = currentIndexPath
|
||||||
|
|
||||||
|
let point = CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y)
|
||||||
|
// ydLog(message: "proposedContentOffset = \(proposedContentOffset)")
|
||||||
|
// ydLog(message: "offsetAdjustment = \(offsetAdjustment)")
|
||||||
|
// ydLog(message: "point = \(point)")
|
||||||
|
// ydLog(message: "currentIndex = \(currentIndex)")
|
||||||
|
|
||||||
|
return point
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
|
||||||
|
guard let collectionView = collectionView else { return nil }
|
||||||
|
|
||||||
|
let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
|
||||||
|
let attributes = super.layoutAttributesForElements(in: rect)?.compactMap { $0.copy() as? UICollectionViewLayoutAttributes }
|
||||||
|
|
||||||
|
for attribute in attributes ?? [] {
|
||||||
|
let distance = visibleRect.midX - attribute.center.x
|
||||||
|
let normalizedDistance = distance / (collectionView.bounds.width * 0.5)
|
||||||
|
let zoom = 1 - abs(normalizedDistance) * minimumScale
|
||||||
|
|
||||||
|
//缩放
|
||||||
|
let scaleTransform = CGAffineTransform(scaleX: zoom, y: zoom)
|
||||||
|
|
||||||
|
|
||||||
|
//旋转
|
||||||
|
let rotationAngle = kSPAngleToRadians(angle: -normalizedDistance * 4)
|
||||||
|
let rotationTransform = CGAffineTransform(rotationAngle: rotationAngle)
|
||||||
|
|
||||||
|
// 组合旋转和缩放变换
|
||||||
|
let combinedTransform = rotationTransform.concatenating(scaleTransform)
|
||||||
|
// 应用变换
|
||||||
|
attribute.transform = combinedTransform
|
||||||
|
|
||||||
|
var alpha = 1.8 - abs(normalizedDistance)
|
||||||
|
if alpha < 0 {
|
||||||
|
alpha = 0
|
||||||
|
} else if alpha > 1 {
|
||||||
|
alpha = 1
|
||||||
|
}
|
||||||
|
attribute.alpha = alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -17,7 +17,7 @@ class SPHomeChildController: SPViewController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func setBgImageView() { }
|
// override func setBgImageView() { }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,14 +8,37 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class SPHomeViewController: SPHomeChildController {
|
class SPHomeViewController: SPHomeChildController {
|
||||||
|
|
||||||
|
|
||||||
///模版数据
|
private lazy var viewModel: SPHomeViewModel = SPHomeViewModel()
|
||||||
private var moduleModel: SPHomeModuleModel?
|
|
||||||
|
|
||||||
|
private lazy var page = 1
|
||||||
|
private lazy var dataArr: [SPShortModel] = []
|
||||||
|
|
||||||
|
//MARK: UI属性
|
||||||
|
private lazy var logoImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: UIImage(named: "logo_icon_01"))
|
||||||
|
imageView.setContentHuggingPriority(.required, for: .horizontal)
|
||||||
|
imageView.setContentCompressionResistancePriority(.required, for: .horizontal)
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var searchButton: SPHomeSearchButton = {
|
||||||
|
let button = SPHomeSearchButton()
|
||||||
|
button.addTarget(self, action: #selector(handleSearchButton), for: .touchUpInside)
|
||||||
|
return button
|
||||||
|
}()
|
||||||
|
|
||||||
private lazy var layout: UICollectionViewFlowLayout = {
|
private lazy var layout: UICollectionViewFlowLayout = {
|
||||||
|
let width = floor((kSPScreenWidth - 32 - 13) / 2)
|
||||||
|
let height = 221 / 165 * width + 44
|
||||||
|
|
||||||
let layout = UICollectionViewFlowLayout()
|
let layout = UICollectionViewFlowLayout()
|
||||||
layout.headerReferenceSize = CGSize(width: kSPScreenWidth, height: 200)
|
layout.itemSize = CGSize(width: width, height: height)
|
||||||
|
layout.headerReferenceSize = CGSize(width: kSPScreenWidth, height: SPHomeHeaderView.contentHeight(viewModel: self.viewModel))
|
||||||
|
layout.sectionInset = .init(top: 0, left: 16, bottom: 0, right: 16)
|
||||||
|
layout.minimumInteritemSpacing = 13
|
||||||
|
layout.minimumLineSpacing = 13
|
||||||
return layout
|
return layout
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -23,23 +46,47 @@ class SPHomeViewController: SPHomeChildController {
|
|||||||
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: layout)
|
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: layout)
|
||||||
collectionView.delegate = self
|
collectionView.delegate = self
|
||||||
collectionView.dataSource = self
|
collectionView.dataSource = self
|
||||||
|
collectionView.sp_addRefreshHeader { [weak self] in
|
||||||
|
self?.handleHeaderRefresh(nil)
|
||||||
|
}
|
||||||
|
collectionView.sp_addRefreshBackFooter(insetBottom: 0) { [weak self] in
|
||||||
|
self?.handleFooterRefresh(nil)
|
||||||
|
}
|
||||||
collectionView.register(SPHomeHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "headerView")
|
collectionView.register(SPHomeHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "headerView")
|
||||||
SPCollectionViewCell.registerCell(collectionView: collectionView)
|
SPHomeShortCell.registerCell(collectionView: collectionView)
|
||||||
return collectionView
|
return collectionView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityDidChangeNotification), name: SPNetworkReachabilityManager.reachabilityDidChangeNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityDidChangeNotification), name: SPNetworkReachabilityManager.reachabilityDidChangeNotification, object: nil)
|
||||||
|
|
||||||
// view.backgroundColor = .clear
|
|
||||||
requestModuleData()
|
requestModuleData()
|
||||||
|
requestPlayHistory()
|
||||||
|
requestListDataArr(page: 1, completer: nil)
|
||||||
|
|
||||||
_setupUI()
|
_setupUI()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func fetchChildControllerScrollView() -> UIScrollView? {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
return self.collectionView
|
super.viewWillAppear(animated)
|
||||||
|
self.navigationController?.setNavigationBarHidden(true, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func handleHeaderRefresh(_ completer: (() -> Void)?) {
|
||||||
|
requestModuleData()
|
||||||
|
requestPlayHistory()
|
||||||
|
requestListDataArr(page: 1) { [weak self] in
|
||||||
|
self?.collectionView.sp_endHeaderRefreshing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func handleFooterRefresh(_ completer: (() -> Void)?) {
|
||||||
|
requestListDataArr(page: self.page + 1) { [weak self] in
|
||||||
|
self?.collectionView.sp_endFooterRefreshing()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -47,42 +94,71 @@ class SPHomeViewController: SPHomeChildController {
|
|||||||
extension SPHomeViewController {
|
extension SPHomeViewController {
|
||||||
|
|
||||||
private func _setupUI() {
|
private func _setupUI() {
|
||||||
view.addSubview(self.collectionView);
|
view.addSubview(logoImageView)
|
||||||
|
view.addSubview(searchButton)
|
||||||
|
view.addSubview(self.collectionView)
|
||||||
|
|
||||||
|
logoImageView.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(16)
|
||||||
|
make.centerY.equalTo(searchButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
searchButton.snp.makeConstraints { make in
|
||||||
|
make.left.equalTo(logoImageView.snp.right).offset(6)
|
||||||
|
make.right.equalToSuperview().offset(-16)
|
||||||
|
make.top.equalToSuperview().offset(kSPStatusbarHeight + 10)
|
||||||
|
}
|
||||||
|
|
||||||
self.collectionView.snp.makeConstraints { make in
|
self.collectionView.snp.makeConstraints { make in
|
||||||
make.left.right.bottom.equalToSuperview()
|
make.left.right.bottom.equalToSuperview()
|
||||||
make.top.equalToSuperview().offset(topMargins)
|
// make.top.equalToSuperview().offset(kSPStatusbarHeight + 66)
|
||||||
|
make.top.equalTo(searchButton.snp.bottom).offset(34)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SPHomeViewController {
|
extension SPHomeViewController {
|
||||||
|
@objc private func handleSearchButton() {
|
||||||
|
let vc = SPSearchViewController()
|
||||||
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func reachabilityDidChangeNotification() {
|
@objc private func reachabilityDidChangeNotification() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
|
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
|
||||||
extension SPHomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
|
extension SPHomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
let cell = SPCollectionViewCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
let cell = SPHomeShortCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
||||||
|
cell.model = dataArr[indexPath.row]
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
return 0
|
return dataArr.count
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
|
||||||
if let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "headerView", for: indexPath) as? SPHomeHeaderView {
|
if let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "headerView", for: indexPath) as? SPHomeHeaderView {
|
||||||
headerView.moduleModel = self.moduleModel
|
headerView.viewModel = self.viewModel
|
||||||
return headerView
|
return headerView
|
||||||
}
|
}
|
||||||
return UICollectionReusableView()
|
return UICollectionReusableView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
let model = self.dataArr[indexPath.row]
|
||||||
|
|
||||||
|
let vc = SPPlayerDetailViewController()
|
||||||
|
vc.shortPlayId = model.short_play_id
|
||||||
|
self.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,11 +169,42 @@ extension SPHomeViewController {
|
|||||||
SPHomeAPI.requestHomeModuleData { [weak self] model in
|
SPHomeAPI.requestHomeModuleData { [weak self] model in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
if let model = model {
|
if let model = model {
|
||||||
self.moduleModel = model
|
self.viewModel.moduleModel = model
|
||||||
self.layout.headerReferenceSize = CGSize(width: kSPScreenWidth, height: SPHomeHeaderView.contentHeight(model: model))
|
|
||||||
|
self.layout.headerReferenceSize = CGSize(width: kSPScreenWidth, height: SPHomeHeaderView.contentHeight(viewModel: self.viewModel))
|
||||||
self.collectionView.reloadData()
|
self.collectionView.reloadData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///获取播放记录
|
||||||
|
private func requestPlayHistory() {
|
||||||
|
SPVideoAPI.requestPlayHistoryList(page: 1) { [weak self] listModel in
|
||||||
|
guard let self = self else { return }
|
||||||
|
if let list = listModel?.list {
|
||||||
|
self.viewModel.playHistoryArr = list
|
||||||
|
self.layout.headerReferenceSize = CGSize(width: kSPScreenWidth, height: SPHomeHeaderView.contentHeight(viewModel: self.viewModel))
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///获取列表数据
|
||||||
|
private func requestListDataArr(page: Int, completer: (() -> Void)?) {
|
||||||
|
SPHomeAPI.requestHomeList(page: page) { [weak self] listModel in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
if let list = listModel?.list {
|
||||||
|
if page == 1 {
|
||||||
|
self.dataArr.removeAll()
|
||||||
|
}
|
||||||
|
self.dataArr += list
|
||||||
|
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
self.page = page
|
||||||
|
}
|
||||||
|
completer?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ class SPHomeModuleModel: SPModel, SmartCodable {
|
|||||||
|
|
||||||
var bannerData: [SPShortModel]?
|
var bannerData: [SPShortModel]?
|
||||||
///首页九宫格数据
|
///首页九宫格数据
|
||||||
var recommandData: [SPShortModel]?
|
var nineSquare: SPHomeNineSquareModel?
|
||||||
///新剧列表
|
///新剧列表
|
||||||
var manualNewestRecommand: [SPShortModel]?
|
var manualNewestRecommand: [SPShortModel]?
|
||||||
///热门排行
|
///热门排行
|
||||||
@ -21,3 +21,9 @@ class SPHomeModuleModel: SPModel, SmartCodable {
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SPHomeNineSquareModel: SPModel, SmartCodable {
|
||||||
|
|
||||||
|
var list: [SPShortModel]?
|
||||||
|
var title: String?
|
||||||
|
}
|
||||||
|
@ -10,7 +10,7 @@ import UIKit
|
|||||||
class SPHomeDataItemView: UIView {
|
class SPHomeDataItemView: UIView {
|
||||||
|
|
||||||
///内容距离顶部位置
|
///内容距离顶部位置
|
||||||
static let contentToTop: CGFloat = 36
|
static let contentToTop: CGFloat = 32
|
||||||
|
|
||||||
|
|
||||||
class func contentHeight(dataArr: [SPShortModel]) -> CGFloat {
|
class func contentHeight(dataArr: [SPShortModel]) -> CGFloat {
|
||||||
@ -30,14 +30,19 @@ class SPHomeDataItemView: UIView {
|
|||||||
|
|
||||||
private(set) lazy var titleLabel: UILabel = {
|
private(set) lazy var titleLabel: UILabel = {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
label.font = .fontMedium(ofSize: 15)
|
label.font = .fontWeight(ofSize: 18, weight: 400)
|
||||||
label.textColor = .colorFFFFFF()
|
label.textColor = .colorFFFFFF()
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private(set) lazy var iconImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView()
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
private lazy var moreButton: UIButton = {
|
private lazy var moreButton: UIButton = {
|
||||||
let button = UIButton(type: .custom)
|
let button = UIButton(type: .custom)
|
||||||
button.setTitle("More", for: .normal)
|
button.setTitle("More".localized, for: .normal)
|
||||||
button.setTitleColor(.colorF564B6(), for: .normal)
|
button.setTitleColor(.colorF564B6(), for: .normal)
|
||||||
button.titleLabel?.font = .fontLight(ofSize: 12)
|
button.titleLabel?.font = .fontLight(ofSize: 12)
|
||||||
return button
|
return button
|
||||||
@ -64,20 +69,26 @@ extension SPHomeDataItemView {
|
|||||||
|
|
||||||
private func _setupUI() {
|
private func _setupUI() {
|
||||||
addSubview(titleLabel)
|
addSubview(titleLabel)
|
||||||
addSubview(moreButton)
|
addSubview(iconImageView)
|
||||||
|
// addSubview(moreButton)
|
||||||
addSubview(contentView)
|
addSubview(contentView)
|
||||||
|
|
||||||
titleLabel.snp.makeConstraints { make in
|
titleLabel.snp.makeConstraints { make in
|
||||||
make.left.equalToSuperview().offset(15)
|
make.left.equalToSuperview().offset(16)
|
||||||
make.top.equalToSuperview()
|
make.top.equalToSuperview()
|
||||||
make.height.equalTo(21)
|
make.height.equalTo(21)
|
||||||
}
|
}
|
||||||
|
|
||||||
moreButton.snp.makeConstraints { make in
|
iconImageView.snp.makeConstraints { make in
|
||||||
make.centerY.equalTo(moreButton)
|
make.centerY.equalTo(titleLabel)
|
||||||
make.right.equalToSuperview().offset(-15)
|
make.left.equalTo(titleLabel.snp.right).offset(8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// moreButton.snp.makeConstraints { make in
|
||||||
|
// make.centerY.equalTo(titleLabel)
|
||||||
|
// make.right.equalToSuperview().offset(-15)
|
||||||
|
// }
|
||||||
|
|
||||||
contentView.snp.makeConstraints { make in
|
contentView.snp.makeConstraints { make in
|
||||||
make.left.right.bottom.equalToSuperview()
|
make.left.right.bottom.equalToSuperview()
|
||||||
make.top.equalTo(Self.contentToTop)
|
make.top.equalTo(Self.contentToTop)
|
||||||
|
65
Thimra/Class/Home/View/SPHomeExploreCell.swift
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// SPHomeExploreCell.swift
|
||||||
|
// Thimra
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeExploreCell: SPCollectionViewCell {
|
||||||
|
|
||||||
|
var model: SPShortModel? {
|
||||||
|
didSet {
|
||||||
|
coverImageView.sp_setImage(url: model?.image_url)
|
||||||
|
titleLabel.text = model?.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: UI属性
|
||||||
|
private lazy var coverImageView: SPImageView = {
|
||||||
|
let imageView = SPImageView()
|
||||||
|
imageView.layer.cornerRadius = 4
|
||||||
|
imageView.layer.masksToBounds = true
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontRegular(ofSize: 12)
|
||||||
|
label.textColor = .colorFFFFFF()
|
||||||
|
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 SPHomeExploreCell {
|
||||||
|
|
||||||
|
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.right.lessThanOrEqualToSuperview()
|
||||||
|
make.top.equalTo(coverImageView.snp.bottom).offset(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
109
Thimra/Class/Home/View/SPHomeExploreView.swift
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
//
|
||||||
|
// SPHomeExploreView.swift
|
||||||
|
// Thimra
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeExploreView: SPHomeDataItemView {
|
||||||
|
|
||||||
|
private static func itemSize() -> CGSize {
|
||||||
|
let width = floor((kSPScreenWidth - 16 * 2 - 14 * 2) / 3)
|
||||||
|
let imageScale: CGFloat = 146 / 105
|
||||||
|
let height = width * imageScale + 36
|
||||||
|
|
||||||
|
return CGSize(width: width, height: height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func contentHeight(dataArr: [SPShortModel]) -> CGFloat {
|
||||||
|
|
||||||
|
var height = self.contentToTop
|
||||||
|
|
||||||
|
var lineCount = dataArr.count / 3
|
||||||
|
if dataArr.count % 3 > 0 {
|
||||||
|
lineCount += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let contentHeight = itemSize().height * CGFloat(lineCount) + 14 * CGFloat(lineCount - 1)
|
||||||
|
if contentHeight > 0 {
|
||||||
|
height += contentHeight
|
||||||
|
} else {
|
||||||
|
height += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return height
|
||||||
|
}
|
||||||
|
|
||||||
|
override var dataArr: [SPShortModel]? {
|
||||||
|
didSet {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: UI属性
|
||||||
|
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.itemSize = Self.itemSize()
|
||||||
|
layout.minimumInteritemSpacing = 14
|
||||||
|
layout.minimumLineSpacing = 14
|
||||||
|
layout.sectionInset = .init(top: 0, left: 16, bottom: 0, right: 16)
|
||||||
|
return layout
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionView: SPCollectionView = {
|
||||||
|
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||||
|
collectionView.delegate = self
|
||||||
|
collectionView.dataSource = self
|
||||||
|
SPHomeExploreCell.registerCell(collectionView: collectionView)
|
||||||
|
return collectionView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
titleLabel.text = "Explore For You".localized
|
||||||
|
iconImageView.image = UIImage(named: "mark_icon_01")
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeExploreView {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(collectionView)
|
||||||
|
|
||||||
|
collectionView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
|
||||||
|
extension SPHomeExploreView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = SPHomeExploreCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
||||||
|
cell.model = dataArr?[indexPath.row]
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return dataArr?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
let model = self.dataArr?[indexPath.row]
|
||||||
|
|
||||||
|
let vc = SPPlayerDetailViewController()
|
||||||
|
vc.shortPlayId = model?.short_play_id
|
||||||
|
self.viewController?.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
}
|
@ -9,27 +9,40 @@ import UIKit
|
|||||||
|
|
||||||
class SPHomeHeaderView: UICollectionReusableView {
|
class SPHomeHeaderView: UICollectionReusableView {
|
||||||
|
|
||||||
var moduleModel: SPHomeModuleModel? {
|
var viewModel: SPHomeViewModel? {
|
||||||
didSet {
|
didSet {
|
||||||
|
let moduleModel = viewModel?.moduleModel
|
||||||
|
|
||||||
stackView.removeAllArrangedSubview()
|
stackView.removeAllArrangedSubview()
|
||||||
|
|
||||||
stackView.addArrangedSubview(bannerView)
|
stackView.addArrangedSubview(bannerView)
|
||||||
bannerView.reloadData()
|
bannerView.reloadData()
|
||||||
|
|
||||||
// if (moduleModel?.recommandData?.count ?? 0) > 0 {
|
if let historyList = viewModel?.playHistoryArr, historyList.count > 0 {
|
||||||
// }
|
stackView.addArrangedSubview(playHistoryView)
|
||||||
stackView.addArrangedSubview(trendingView)
|
playHistoryView.dataArr = historyList
|
||||||
trendingView.dataArr = moduleModel?.bannerData
|
}
|
||||||
|
|
||||||
stackView.addArrangedSubview(hotView)
|
if let list = moduleModel?.nineSquare?.list, list.count > 0 {
|
||||||
hotView.dataArr = moduleModel?.bannerData
|
stackView.addArrangedSubview(exploreView)
|
||||||
|
exploreView.titleLabel.text = moduleModel?.nineSquare?.title
|
||||||
stackView.addArrangedSubview(shortsForYouView)
|
exploreView.dataArr = list
|
||||||
shortsForYouView.dataArr = moduleModel?.bannerData
|
}
|
||||||
|
|
||||||
|
// stackView.addArrangedSubview(trendingView)
|
||||||
|
// trendingView.dataArr = moduleModel?.bannerData
|
||||||
|
//
|
||||||
|
// stackView.addArrangedSubview(hotView)
|
||||||
|
// hotView.dataArr = moduleModel?.bannerData
|
||||||
|
//
|
||||||
|
// stackView.addArrangedSubview(shortsForYouView)
|
||||||
|
// shortsForYouView.dataArr = moduleModel?.bannerData
|
||||||
|
|
||||||
|
stackView.addArrangedSubview(titleView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private lazy var stackView: UIStackView = {
|
private lazy var stackView: UIStackView = {
|
||||||
let view = UIStackView()
|
let view = UIStackView()
|
||||||
view.axis = .vertical
|
view.axis = .vertical
|
||||||
@ -37,19 +50,37 @@ class SPHomeHeaderView: UICollectionReusableView {
|
|||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var bannerView: ZKCycleScrollView = {
|
private lazy var bannerLayout: SPZoomCollectionViewLayout = {
|
||||||
let bannerView = ZKCycleScrollView(frame: .zero, shouldInfiniteLoop: true);
|
let layout = SPZoomCollectionViewLayout()
|
||||||
bannerView.delegate = self
|
layout.itemSize = .init(width: 234, height: Self.bannerHeight())
|
||||||
bannerView.dataSource = self
|
layout.minimumLineSpacing = 10
|
||||||
bannerView.itemSpacing = 10
|
layout.minimumInteritemSpacing = 0
|
||||||
bannerView.itemSize = CGSize(width: kSPScreenWidth - 30, height: Self.bannerHeight())
|
return layout
|
||||||
bannerView.register(SPHomeBannerCell.self, forCellWithReuseIdentifier: "bannerCell")
|
}()
|
||||||
bannerView.hidesPageControl = true
|
|
||||||
bannerView.snp.makeConstraints { make in
|
private lazy var bannerView: SPCollectionView = {
|
||||||
|
let view = SPCollectionView(frame: .zero, collectionViewLayout: bannerLayout)
|
||||||
|
view.delegate = self
|
||||||
|
view.dataSource = self
|
||||||
|
view.showsVerticalScrollIndicator = false
|
||||||
|
view.showsHorizontalScrollIndicator = false
|
||||||
|
SPHomeZoomBannerCell.registerCell(collectionView: view)
|
||||||
|
|
||||||
|
view.snp.makeConstraints { make in
|
||||||
make.width.equalTo(kSPScreenWidth)
|
make.width.equalTo(kSPScreenWidth)
|
||||||
make.height.equalTo(Self.bannerHeight())
|
make.height.equalTo(Self.bannerHeight())
|
||||||
}
|
}
|
||||||
return bannerView
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var playHistoryView: SPHomePlayHistoryView = {
|
||||||
|
let view = SPHomePlayHistoryView()
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var exploreView: SPHomeExploreView = {
|
||||||
|
let view = SPHomeExploreView()
|
||||||
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var trendingView: SPHomeTrendingView = {
|
private lazy var trendingView: SPHomeTrendingView = {
|
||||||
@ -67,6 +98,12 @@ class SPHomeHeaderView: UICollectionReusableView {
|
|||||||
return view
|
return view
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private lazy var titleView: SPHomeDataItemView = {
|
||||||
|
let view = SPHomeDataItemView()
|
||||||
|
view.titleLabel.text = "More for you!".localized
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
_setupUI()
|
_setupUI()
|
||||||
@ -90,50 +127,65 @@ extension SPHomeHeaderView {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: -------------- ZKCycleScrollViewDelegate & ZKCycleScrollViewDataSource --------------
|
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
|
||||||
extension SPHomeHeaderView: ZKCycleScrollViewDelegate, ZKCycleScrollViewDataSource {
|
extension SPHomeHeaderView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
|
||||||
func numberOfItems(in cycleScrollView: ZKCycleScrollView) -> Int {
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
return moduleModel?.bannerData?.count ?? 0
|
let cell = SPHomeZoomBannerCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
||||||
}
|
cell.model = self.viewModel?.moduleModel?.bannerData?[indexPath.row]
|
||||||
|
|
||||||
func cycleScrollView(_ cycleScrollView: ZKCycleScrollView, cellForItemAt index: Int) -> ZKCycleScrollViewCell {
|
|
||||||
let cell = cycleScrollView.dequeueReusableCell(withReuseIdentifier: "bannerCell", for: index) as! SPHomeBannerCell
|
|
||||||
cell.model = moduleModel?.bannerData?[index]
|
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
func cycleScrollView(_ cycleScrollView: ZKCycleScrollView, didSelectItemAt index: Int) {
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
let model = moduleModel?.bannerData?[index]
|
return self.viewModel?.moduleModel?.bannerData?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func numberOfSections(in collectionView: UICollectionView) -> Int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
let model = self.viewModel?.moduleModel?.bannerData?[indexPath.row]
|
||||||
|
|
||||||
let vc = SPPlayerDetailViewController()
|
let vc = SPPlayerDetailViewController()
|
||||||
vc.shortPlayId = model?.short_play_id
|
vc.shortPlayId = model?.short_play_id
|
||||||
self.viewController?.navigationController?.pushViewController(vc, animated: true)
|
self.viewController?.navigationController?.pushViewController(vc, animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension SPHomeHeaderView {
|
extension SPHomeHeaderView {
|
||||||
|
|
||||||
static func contentHeight(model: SPHomeModuleModel) -> CGFloat {
|
static func contentHeight(viewModel: SPHomeViewModel) -> CGFloat {
|
||||||
var height = bannerHeight()
|
var height = bannerHeight()
|
||||||
|
let moduleModel = viewModel.moduleModel
|
||||||
|
|
||||||
// if (model.recommandData?.count ?? 0) > 0 {
|
///历史记录
|
||||||
// }
|
if let historyList = viewModel.playHistoryArr, historyList.count > 0 {
|
||||||
height = height + SPHomeTrendingView.contentHeight(dataArr: model.bannerData ?? []) + 25
|
height = height + SPHomePlayHistoryView.contentHeight(dataArr: historyList) + 25
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
height = height + SPHomeHotView.contentHeight(dataArr: model.bannerData ?? []) + 25
|
///九宫格数据
|
||||||
|
if let list = moduleModel?.nineSquare?.list, list.count > 0 {
|
||||||
|
height = height + SPHomeExploreView.contentHeight(dataArr: list) + 25
|
||||||
|
}
|
||||||
|
|
||||||
height = height + SPHomeShortsForYouView.contentHeight(dataArr: model.bannerData ?? []) + 25
|
// height = height + SPHomeTrendingView.contentHeight(dataArr: moduleModel?.bannerData ?? []) + 25
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// height = height + SPHomeHotView.contentHeight(dataArr: moduleModel?.bannerData ?? []) + 25
|
||||||
|
//
|
||||||
|
// height = height + SPHomeShortsForYouView.contentHeight(dataArr: moduleModel?.bannerData ?? []) + 25
|
||||||
|
|
||||||
|
///标题高度
|
||||||
|
height = height + SPHomeDataItemView.contentHeight(dataArr: []) + 25
|
||||||
|
|
||||||
return height
|
return height
|
||||||
}
|
}
|
||||||
|
|
||||||
static func bannerHeight() -> CGFloat {
|
static func bannerHeight() -> CGFloat {
|
||||||
return 183
|
return 336
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
116
Thimra/Class/Home/View/SPHomePlayHistoryCell.swift
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
//
|
||||||
|
// SPHomePlayHistoryCell.swift
|
||||||
|
// Thimra
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomePlayHistoryCell: SPCollectionViewCell {
|
||||||
|
|
||||||
|
var model: SPShortModel? {
|
||||||
|
didSet {
|
||||||
|
coverImageView.sp_setImage(url: model?.image_url)
|
||||||
|
|
||||||
|
titleLabel.text = model?.name
|
||||||
|
|
||||||
|
// model?.episode_total
|
||||||
|
|
||||||
|
let episode_total = "\(model?.episode_total ?? 0)"
|
||||||
|
let episode = String(format: "EP.%@/%@", "\(model?.current_episode ?? "0")", episode_total)
|
||||||
|
|
||||||
|
let episodeString = NSMutableAttributedString(string: episode)
|
||||||
|
episodeString.color = .colorFF5100()
|
||||||
|
|
||||||
|
let range = NSRange(location: episode.length() - episode_total.length() - 1, length: episode_total.length() + 1)
|
||||||
|
episodeString.setColor(.colorAFAFAF(), range: range)
|
||||||
|
episodeLabel.attributedText = episodeString
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var coverImageView: SPImageView = {
|
||||||
|
let imageView = SPImageView()
|
||||||
|
imageView.layer.cornerRadius = 44
|
||||||
|
imageView.layer.masksToBounds = true
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
///蒙层
|
||||||
|
private lazy var maskLayerView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.backgroundColor = .color000000(alpha: 0.3)
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var playImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: UIImage(named: "play_icon_04"))
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontWeight(ofSize: 12, weight: 200)
|
||||||
|
label.textColor = .colorFFFFFF()
|
||||||
|
label.numberOfLines = 2
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var episodeLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontRegular(ofSize: 10)
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomePlayHistoryCell {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(coverImageView)
|
||||||
|
coverImageView.addSubview(maskLayerView)
|
||||||
|
coverImageView.addSubview(playImageView)
|
||||||
|
contentView.addSubview(titleLabel)
|
||||||
|
contentView.addSubview(episodeLabel)
|
||||||
|
|
||||||
|
coverImageView.snp.makeConstraints { make in
|
||||||
|
make.top.equalToSuperview()
|
||||||
|
make.centerX.equalToSuperview()
|
||||||
|
make.width.height.equalTo(88)
|
||||||
|
}
|
||||||
|
|
||||||
|
maskLayerView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
playImageView.snp.makeConstraints { make in
|
||||||
|
make.center.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview()
|
||||||
|
make.top.equalTo(coverImageView.snp.bottom).offset(6)
|
||||||
|
make.right.lessThanOrEqualToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
episodeLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview()
|
||||||
|
make.bottom.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
91
Thimra/Class/Home/View/SPHomePlayHistoryView.swift
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
//
|
||||||
|
// SPHomePlayHistoryView.swift
|
||||||
|
// Thimra
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomePlayHistoryView: SPHomeDataItemView {
|
||||||
|
|
||||||
|
override class func contentHeight(dataArr: [SPShortModel]) -> CGFloat {
|
||||||
|
let height = self.contentToTop + 136
|
||||||
|
return height
|
||||||
|
}
|
||||||
|
|
||||||
|
override var dataArr: [SPShortModel]? {
|
||||||
|
didSet {
|
||||||
|
self.collectionView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||||
|
let layout = UICollectionViewFlowLayout()
|
||||||
|
layout.scrollDirection = .horizontal
|
||||||
|
layout.itemSize = CGSize(width: 88, height: 136)
|
||||||
|
layout.sectionInset = .init(top: 0, left: 16, bottom: 0, right: 16)
|
||||||
|
layout.minimumInteritemSpacing = 16
|
||||||
|
layout.minimumLineSpacing = 16
|
||||||
|
return layout
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var collectionView: SPCollectionView = {
|
||||||
|
let collectionView = SPCollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||||
|
collectionView.delegate = self
|
||||||
|
collectionView.dataSource = self
|
||||||
|
collectionView.showsVerticalScrollIndicator = false
|
||||||
|
collectionView.showsHorizontalScrollIndicator = false
|
||||||
|
SPHomePlayHistoryCell.registerCell(collectionView: collectionView)
|
||||||
|
return collectionView
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
self.titleLabel.text = "Continue watching".localized
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomePlayHistoryView {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(self.collectionView)
|
||||||
|
|
||||||
|
self.collectionView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource --------------
|
||||||
|
extension SPHomePlayHistoryView: UICollectionViewDelegate, UICollectionViewDataSource {
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
||||||
|
let cell = SPHomePlayHistoryCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath)
|
||||||
|
cell.model = dataArr?[indexPath.row]
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
|
return dataArr?.count ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
|
let model = dataArr?[indexPath.row]
|
||||||
|
|
||||||
|
let vc = SPPlayerDetailViewController()
|
||||||
|
vc.shortPlayId = model?.short_play_id
|
||||||
|
vc.playHistoryModel = model
|
||||||
|
self.viewController?.navigationController?.pushViewController(vc, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,7 +11,7 @@ class SPHomeSearchButton: UIControl {
|
|||||||
|
|
||||||
|
|
||||||
override var intrinsicContentSize: CGSize {
|
override var intrinsicContentSize: CGSize {
|
||||||
return CGSize(width: kSPScreenWidth, height: 38)
|
return CGSize(width: kSPScreenWidth, height: 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
private lazy var iconImageView: UIImageView = {
|
private lazy var iconImageView: UIImageView = {
|
||||||
@ -19,9 +19,17 @@ class SPHomeSearchButton: UIControl {
|
|||||||
return imageView
|
return imageView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private lazy var textLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontRegular(ofSize: 12)
|
||||||
|
label.textColor = .colorFFFFFF(alpha: 0.52)
|
||||||
|
label.text = "Love Me Like You Do It".localized
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
layer.cornerRadius = 19
|
layer.cornerRadius = 16
|
||||||
layer.masksToBounds = true
|
layer.masksToBounds = true
|
||||||
|
|
||||||
backgroundColor = .colorFFFFFF(alpha: 0.1)
|
backgroundColor = .colorFFFFFF(alpha: 0.1)
|
||||||
@ -38,11 +46,18 @@ class SPHomeSearchButton: UIControl {
|
|||||||
extension SPHomeSearchButton {
|
extension SPHomeSearchButton {
|
||||||
|
|
||||||
private func _setupUI() {
|
private func _setupUI() {
|
||||||
|
addSubview(textLabel)
|
||||||
addSubview(iconImageView)
|
addSubview(iconImageView)
|
||||||
|
|
||||||
|
textLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(14)
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
make.right.lessThanOrEqualTo(self.iconImageView.snp.left).offset(-5)
|
||||||
|
}
|
||||||
|
|
||||||
iconImageView.snp.makeConstraints { make in
|
iconImageView.snp.makeConstraints { make in
|
||||||
make.centerY.equalToSuperview()
|
make.centerY.equalToSuperview()
|
||||||
make.left.equalToSuperview().offset(15)
|
make.right.equalToSuperview().offset(-6)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
64
Thimra/Class/Home/View/SPHomeShortCell.swift
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// SPHomeShortCell.swift
|
||||||
|
// Thimra
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeShortCell: SPCollectionViewCell {
|
||||||
|
|
||||||
|
var model: SPShortModel? {
|
||||||
|
didSet {
|
||||||
|
coverImageView.sp_setImage(url: model?.image_url)
|
||||||
|
titleLabel.text = model?.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var coverImageView: SPImageView = {
|
||||||
|
let imageView = SPImageView()
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontMedium(ofSize: 12)
|
||||||
|
label.textColor = .colorFFFFFF()
|
||||||
|
label.numberOfLines = 2
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
contentView.layer.cornerRadius = 8
|
||||||
|
contentView.layer.masksToBounds = true
|
||||||
|
contentView.backgroundColor = .colorD9D9D9(alpha: 0.14)
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeShortCell {
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(coverImageView)
|
||||||
|
contentView.addSubview(titleLabel)
|
||||||
|
|
||||||
|
coverImageView.snp.makeConstraints { make in
|
||||||
|
make.left.right.top.equalToSuperview()
|
||||||
|
make.bottom.equalToSuperview().offset(-44)
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(8)
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-8)
|
||||||
|
make.top.equalTo(coverImageView.snp.bottom).offset(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
86
Thimra/Class/Home/View/SPHomeZoomBannerCell.swift
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
//
|
||||||
|
// SPHomeZoomBannerCell.swift
|
||||||
|
// Thimra
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeZoomBannerCell: SPCollectionViewCell {
|
||||||
|
|
||||||
|
var model: SPShortModel? {
|
||||||
|
didSet {
|
||||||
|
coverImageView.sp_setImage(url: model?.image_url)
|
||||||
|
titleLabel.text = model?.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var coverImageView: SPImageView = {
|
||||||
|
let imageView = SPImageView()
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var iconImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView(image: UIImage(named: "new_icon_01"))
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var bottomView: UIView = {
|
||||||
|
let view = UIView()
|
||||||
|
view.addEffectView(style: .light)
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = .fontRegular(ofSize: 16)
|
||||||
|
label.textColor = .colorFFFFFF()
|
||||||
|
label.numberOfLines = 2
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
contentView.layer.cornerRadius = 8
|
||||||
|
contentView.layer.masksToBounds = true
|
||||||
|
|
||||||
|
_setupUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SPHomeZoomBannerCell {
|
||||||
|
|
||||||
|
private func _setupUI() {
|
||||||
|
contentView.addSubview(coverImageView)
|
||||||
|
coverImageView.addSubview(iconImageView)
|
||||||
|
coverImageView.addSubview(bottomView)
|
||||||
|
bottomView.addSubview(titleLabel)
|
||||||
|
|
||||||
|
coverImageView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
iconImageView.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(12)
|
||||||
|
make.top.equalToSuperview().offset(12)
|
||||||
|
}
|
||||||
|
|
||||||
|
bottomView.snp.makeConstraints { make in
|
||||||
|
make.left.right.bottom.equalToSuperview()
|
||||||
|
make.height.equalTo(72)
|
||||||
|
}
|
||||||
|
|
||||||
|
titleLabel.snp.makeConstraints { make in
|
||||||
|
make.left.equalToSuperview().offset(15)
|
||||||
|
make.right.lessThanOrEqualToSuperview().offset(-15)
|
||||||
|
make.centerY.equalToSuperview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
Thimra/Class/Home/ViewModel/SPHomeViewModel.swift
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// SPHomeViewModel.swift
|
||||||
|
// Thimra
|
||||||
|
//
|
||||||
|
// Created by Overseas on 2025/4/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class SPHomeViewModel: NSObject {
|
||||||
|
|
||||||
|
var moduleModel: SPHomeModuleModel?
|
||||||
|
|
||||||
|
///历史记录
|
||||||
|
var playHistoryArr: [SPShortModel]?
|
||||||
|
|
||||||
|
}
|
@ -62,6 +62,11 @@ class SPEpisodeView: HWPanModalContentView {
|
|||||||
var isDragging = false
|
var isDragging = false
|
||||||
|
|
||||||
//MARK: UI属性
|
//MARK: UI属性
|
||||||
|
private lazy var bgView: UIView = {
|
||||||
|
let view = UIImageView(image: UIImage(named: "episode_bg_image_01"))
|
||||||
|
return view
|
||||||
|
}()
|
||||||
|
|
||||||
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
|
||||||
let itemWidth = floor((kSPScreenWidth - 10 * 4 - 30) / 5)
|
let itemWidth = floor((kSPScreenWidth - 10 * 4 - 30) / 5)
|
||||||
|
|
||||||
@ -91,23 +96,25 @@ class SPEpisodeView: HWPanModalContentView {
|
|||||||
|
|
||||||
private lazy var coverImageView: SPImageView = {
|
private lazy var coverImageView: SPImageView = {
|
||||||
let imageView = SPImageView()
|
let imageView = SPImageView()
|
||||||
imageView.layer.cornerRadius = 6
|
imageView.layer.cornerRadius = 4
|
||||||
imageView.layer.masksToBounds = true
|
imageView.layer.masksToBounds = true
|
||||||
|
imageView.layer.borderColor = UIColor.colorFFFFFF(alpha: 0.26).cgColor
|
||||||
|
imageView.layer.borderWidth = 1
|
||||||
return imageView
|
return imageView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var titleLabel: UILabel = {
|
private lazy var titleLabel: UILabel = {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
label.font = .fontMedium(ofSize: 15)
|
label.font = .fontMedium(ofSize: 14)
|
||||||
label.textColor = .colorFFFFFF()
|
label.textColor = .colorFFFFFF()
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var desLabel: UILabel = {
|
private lazy var desLabel: UILabel = {
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
label.font = .fontLight(ofSize: 12)
|
label.font = .fontRegular(ofSize: 10)
|
||||||
label.textColor = .color9D9D9D()
|
label.textColor = .colorA8A5AA()
|
||||||
label.numberOfLines = 0
|
label.numberOfLines = 5
|
||||||
return label
|
return label
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -165,10 +172,10 @@ class SPEpisodeView: HWPanModalContentView {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
override func present(in view: UIView?) {
|
// override func present(in view: UIView?) {
|
||||||
super.present(in: view)
|
// super.present(in: view)
|
||||||
self.hw_contentView.addEffectView(style: .dark)
|
// self.hw_contentView.addEffectView(style: .dark)
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -177,6 +184,7 @@ class SPEpisodeView: HWPanModalContentView {
|
|||||||
extension SPEpisodeView {
|
extension SPEpisodeView {
|
||||||
|
|
||||||
private func _setupUI() {
|
private func _setupUI() {
|
||||||
|
addSubview(bgView)
|
||||||
addSubview(indicatorView)
|
addSubview(indicatorView)
|
||||||
addSubview(coverImageView)
|
addSubview(coverImageView)
|
||||||
addSubview(titleLabel)
|
addSubview(titleLabel)
|
||||||
@ -185,6 +193,11 @@ extension SPEpisodeView {
|
|||||||
addSubview(lineView)
|
addSubview(lineView)
|
||||||
addSubview(collectionView)
|
addSubview(collectionView)
|
||||||
|
|
||||||
|
|
||||||
|
bgView.snp.makeConstraints { make in
|
||||||
|
make.edges.equalToSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
self.indicatorView.snp.makeConstraints { make in
|
self.indicatorView.snp.makeConstraints { make in
|
||||||
make.centerX.equalToSuperview()
|
make.centerX.equalToSuperview()
|
||||||
make.top.equalToSuperview().offset(15)
|
make.top.equalToSuperview().offset(15)
|
||||||
@ -195,18 +208,18 @@ extension SPEpisodeView {
|
|||||||
self.coverImageView.snp.makeConstraints { make in
|
self.coverImageView.snp.makeConstraints { make in
|
||||||
make.left.equalToSuperview().offset(15)
|
make.left.equalToSuperview().offset(15)
|
||||||
make.top.equalToSuperview().offset(39)
|
make.top.equalToSuperview().offset(39)
|
||||||
make.width.equalTo(55)
|
make.width.equalTo(70)
|
||||||
make.height.equalTo(74)
|
make.height.equalTo(104)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.titleLabel.snp.makeConstraints { make in
|
self.titleLabel.snp.makeConstraints { make in
|
||||||
make.left.equalTo(self.coverImageView.snp.right).offset(12)
|
make.left.equalTo(self.coverImageView.snp.right).offset(12)
|
||||||
make.right.lessThanOrEqualToSuperview().offset(-15)
|
make.right.lessThanOrEqualToSuperview().offset(-16)
|
||||||
make.centerY.equalTo(self.coverImageView)
|
make.top.equalTo(self.coverImageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.desLabel.snp.makeConstraints { make in
|
self.desLabel.snp.makeConstraints { make in
|
||||||
make.left.equalTo(self.coverImageView)
|
make.left.equalTo(self.titleLabel)
|
||||||
make.right.lessThanOrEqualToSuperview().offset(-15)
|
make.right.lessThanOrEqualToSuperview().offset(-15)
|
||||||
make.top.equalTo(self.coverImageView.snp.bottom).offset(8)
|
make.top.equalTo(self.coverImageView.snp.bottom).offset(8)
|
||||||
}
|
}
|
||||||
|
22
Thimra/Source/Assets.xcassets/icon/logo_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame 155@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame 155@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Thimra/Source/Assets.xcassets/icon/logo_icon_01.imageset/Frame 155@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Thimra/Source/Assets.xcassets/icon/logo_icon_01.imageset/Frame 155@3x.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
22
Thimra/Source/Assets.xcassets/icon/mark_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "💗@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "💗@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Thimra/Source/Assets.xcassets/icon/mark_icon_01.imageset/💗@2x.png
vendored
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
Thimra/Source/Assets.xcassets/icon/mark_icon_01.imageset/💗@3x.png
vendored
Normal file
After Width: | Height: | Size: 5.2 KiB |
22
Thimra/Source/Assets.xcassets/icon/new_icon_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame 127@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame 127@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Thimra/Source/Assets.xcassets/icon/new_icon_01.imageset/Frame 127@2x.png
vendored
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
Thimra/Source/Assets.xcassets/icon/new_icon_01.imageset/Frame 127@3x.png
vendored
Normal file
After Width: | Height: | Size: 12 KiB |
22
Thimra/Source/Assets.xcassets/icon/play_icon_04.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Frame@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Thimra/Source/Assets.xcassets/icon/play_icon_04.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 502 B |
BIN
Thimra/Source/Assets.xcassets/icon/play_icon_04.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 686 B |
@ -5,12 +5,12 @@
|
|||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "搜索图标@2x.png",
|
"filename" : "Frame@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "搜索图标@3x.png",
|
"filename" : "Frame@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
|
BIN
Thimra/Source/Assets.xcassets/icon/search_icon_01.imageset/Frame@2x.png
vendored
Normal file
After Width: | Height: | Size: 537 B |
BIN
Thimra/Source/Assets.xcassets/icon/search_icon_01.imageset/Frame@3x.png
vendored
Normal file
After Width: | Height: | Size: 775 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.8 KiB |
22
Thimra/Source/Assets.xcassets/image/episode_bg_image_01.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "背景@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "背景@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
Thimra/Source/Assets.xcassets/image/episode_bg_image_01.imageset/背景@2x.png
vendored
Normal file
After Width: | Height: | Size: 249 KiB |
BIN
Thimra/Source/Assets.xcassets/image/episode_bg_image_01.imageset/背景@3x.png
vendored
Normal file
After Width: | Height: | Size: 554 KiB |
@ -33,6 +33,11 @@
|
|||||||
"Cancel" = "Cancel";
|
"Cancel" = "Cancel";
|
||||||
"Select All" = "Select All";
|
"Select All" = "Select All";
|
||||||
"Delet (%@)" = "Delet (%@)";
|
"Delet (%@)" = "Delet (%@)";
|
||||||
|
"Love Me Like You Do It" = "Love Me Like You Do It";
|
||||||
|
"Explore For You" = "Explore For You";
|
||||||
|
"Continue watching" = "Continue watching";
|
||||||
|
"More for you!" = "More for you!";
|
||||||
|
"More" = "More";
|
||||||
|
|
||||||
///视频详情标题
|
///视频详情标题
|
||||||
"kPlayerDetailTitleString" = "Episode %@ / %@";
|
"kPlayerDetailTitleString" = "Episode %@ / %@";
|
||||||
|