角色list
This commit is contained in:
parent
2c6bb330af
commit
8f61a1e969
|
|
@ -18,42 +18,58 @@ final class TopHeaderManager {
|
|||
return view
|
||||
}()
|
||||
|
||||
private var hostingView: UIView?
|
||||
private var topConstraint: Constraint?
|
||||
private var heightConstraint: Constraint?
|
||||
|
||||
private init() {}
|
||||
|
||||
var jumpPublisher: AnyPublisher<JumpTarget, Never> {
|
||||
headerView.jumpPublisher
|
||||
}
|
||||
|
||||
func attachIfNeeded(to viewController: UIViewController) {
|
||||
guard hostingView !== viewController.view else { return }
|
||||
detach()
|
||||
let container = viewController.view!
|
||||
container.addSubview(headerView)
|
||||
headerView.snp.makeConstraints { make in
|
||||
topConstraint = make.top.equalToSuperview().constraint
|
||||
make.leading.trailing.equalToSuperview()
|
||||
heightConstraint = make.height.equalTo(UIDevice().navHeight).constraint
|
||||
// 为传入的视图创建并附加一个新的 CLTopHeaderView。
|
||||
// 若视图已拥有 CLTopHeaderView,则直接返回已有实例,避免重复添加。
|
||||
@discardableResult
|
||||
func buildAndAttach(to superView: UIView) -> CLTopHeaderView {
|
||||
if let existing = superView.subviews.first(where: { $0 is CLTopHeaderView }) as? CLTopHeaderView {
|
||||
return existing
|
||||
}
|
||||
hostingView = container
|
||||
headerView.isHidden = false
|
||||
let header = CLTopHeaderView()
|
||||
superView.addSubview(header)
|
||||
header.snp.makeConstraints { make in
|
||||
make.left.right.top.equalToSuperview()
|
||||
make.height.equalTo(UIDevice().navHeight)
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
func show() {
|
||||
headerView.isHidden = false
|
||||
}
|
||||
|
||||
func hide() {
|
||||
headerView.isHidden = true
|
||||
}
|
||||
|
||||
func detach() {
|
||||
headerView.removeFromSuperview()
|
||||
hostingView = nil
|
||||
}
|
||||
// private var hostingView: UIView?
|
||||
// private var topConstraint: Constraint?
|
||||
// private var heightConstraint: Constraint?
|
||||
//
|
||||
// private init() {}
|
||||
//
|
||||
// var jumpPublisher: AnyPublisher<JumpTarget, Never> {
|
||||
// headerView.jumpPublisher
|
||||
// }
|
||||
//
|
||||
// func attachIfNeeded(to viewController: UIViewController) {
|
||||
// guard hostingView !== viewController.view else { return }
|
||||
// detach()
|
||||
// let container = viewController.view!
|
||||
// container.addSubview(headerView)
|
||||
// headerView.snp.makeConstraints { make in
|
||||
// topConstraint = make.top.equalToSuperview().constraint
|
||||
// make.leading.trailing.equalToSuperview()
|
||||
// heightConstraint = make.height.equalTo(UIDevice().navHeight).constraint
|
||||
// }
|
||||
// hostingView = container
|
||||
// headerView.isHidden = false
|
||||
// }
|
||||
//
|
||||
// func show() {
|
||||
// headerView.isHidden = false
|
||||
// }
|
||||
//
|
||||
// func hide() {
|
||||
// headerView.isHidden = true
|
||||
// }
|
||||
//
|
||||
// func detach() {
|
||||
// headerView.removeFromSuperview()
|
||||
// hostingView = nil
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,17 +27,27 @@ struct RoleListModel: Codable {
|
|||
var rows: [RoleItem] = []
|
||||
}
|
||||
|
||||
enum RoleSourceType: Int, Codable {
|
||||
case novel
|
||||
case video
|
||||
case other
|
||||
}
|
||||
|
||||
struct RoleItem: Codable {
|
||||
var id: String = ""
|
||||
var name: String = ""
|
||||
var coverImage: String = ""
|
||||
var id: String?
|
||||
var name: String?
|
||||
var coverImage: String?
|
||||
// 来源封面
|
||||
var sourceCoverImage: String?
|
||||
var sourceName: String?
|
||||
// 角色头像
|
||||
var headPortrait: String = ""
|
||||
var headPortrait: String?
|
||||
var score: Double?
|
||||
var description: String = ""
|
||||
var updateTime: String = ""
|
||||
var sourceId: String = ""
|
||||
var sourceType: Int = 0
|
||||
var description: String?
|
||||
var updateTime: String?
|
||||
var sourceId: String?
|
||||
// 0:小说;1:漫剧;2:用户自定义
|
||||
var sourceType: RoleSourceType?
|
||||
var commonCount: Int?
|
||||
var tags: [tagItem] = []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ class FriendsRootHomeController: CLTabRootController<FriendsRootHomeView> {
|
|||
|
||||
// view.showEmpty(text: "Friends Coming soon")
|
||||
|
||||
// 每个 Tab 子控制器拥有自己的 Header,布局一致
|
||||
_ = TopHeaderManager.shared.buildAndAttach(to: view)
|
||||
|
||||
setupViews()
|
||||
setupDats()
|
||||
setupEvents()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ class HomePageRootController: CLTabRootController<HomePageRootView> {
|
|||
|
||||
//container.viewModel = viewModel
|
||||
container.setupViewModel(vm: viewModel)
|
||||
// 每个 Tab 子控制器拥有自己的 Header,布局一致
|
||||
_ = TopHeaderManager.shared.buildAndAttach(to: view)
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
// view.showEmpty(text: "首页 Coming soon")
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class HomePageRootView: UIView {
|
|||
}
|
||||
|
||||
private func setupViews() {
|
||||
// TopHeaderManager.shared.addFromSuperView(self)
|
||||
|
||||
emptyView = {
|
||||
let v = UIView()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ class MeRootPageController: CLTabRootController<MeRootPageView> {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// 每个 Tab 子控制器拥有自己的 Header,布局一致
|
||||
_ = TopHeaderManager.shared.buildAndAttach(to: view)
|
||||
|
||||
setupViews()
|
||||
setupDats()
|
||||
setupEvents()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ class CLRoleCollectionCell: UICollectionViewCell {
|
|||
|
||||
lazy var bookImgView: UIImageView = {
|
||||
let imgView = UIImageView()
|
||||
imgView.backgroundColor = .blue
|
||||
imgView.cornerRadius = 7
|
||||
return imgView
|
||||
}()
|
||||
|
|
@ -135,12 +134,13 @@ class CLRoleCollectionCell: UICollectionViewCell {
|
|||
func setupData(item: RoleItem) {
|
||||
descLab.text = item.description
|
||||
nameLab.text = item.name
|
||||
coverImgView.sd_setImage(with: URL(string: item.coverImage), placeholderImage: nil)
|
||||
tagLab.text = item.tags.compactMap { $0.name }.joined(separator: "/")
|
||||
// 0:小说;1:漫剧;2:用户自定义
|
||||
self.bookBgImgView.isHidden = item.sourceType != 0
|
||||
self.playImgView.isHidden = item.sourceType != 1
|
||||
// coverImgView.
|
||||
coverImgView.sd_setImage(with: URL(string: item.coverImage ?? ""), placeholderImage: nil)
|
||||
bookImgView.sd_setImage(with: URL(string: item.sourceCoverImage ?? ""), placeholderImage: nil)
|
||||
tagLab.text = item.tags.compactMap { "#\($0.name)" }.joined(separator: "/")
|
||||
sourceLab.text = item.score?.truncateString(places: 1)
|
||||
self.bookBgImgView.isHidden = item.sourceType != .novel
|
||||
self.playImgView.isHidden = item.sourceType != .video
|
||||
self.remindLab.text = item.sourceName
|
||||
}
|
||||
|
||||
// MARK: subviews
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import Combine
|
|||
class RolesRootPageView: CLContainer {
|
||||
|
||||
let itemWidth: CGFloat = (UIScreen.width - 30.0) / 2.0
|
||||
var jumpPublisher: AnyPublisher<JumpTarget, Never> { topView.jumpPublisher }
|
||||
var jumpPublisher: AnyPublisher<JumpTarget, Never> { TopHeaderManager.shared.buildAndAttach(to: self).jumpPublisher }
|
||||
|
||||
// private lazy var pagingView = JXPagingListRefreshView(delegate: self)
|
||||
// lazy var headerView:
|
||||
|
|
@ -25,33 +25,10 @@ class RolesRootPageView: CLContainer {
|
|||
var data: [RoleItem] = []
|
||||
var hasMoreData: Bool = true // 是否还有更多数据
|
||||
|
||||
let remind: [String] = [
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]",
|
||||
"[The Lsat Oracle of Kael]"
|
||||
]
|
||||
|
||||
lazy var topView: CLTopHeaderView = {
|
||||
let view = CLTopHeaderView()
|
||||
return view
|
||||
}()
|
||||
// lazy var topView: CLTopHeaderView = {
|
||||
// let view = CLTopHeaderView()
|
||||
// return view
|
||||
// }()
|
||||
|
||||
lazy var tagsView: CLRoleTagsView = {
|
||||
let tagsView = CLRoleTagsView()
|
||||
|
|
@ -85,19 +62,13 @@ class RolesRootPageView: CLContainer {
|
|||
}
|
||||
|
||||
private func setupViews() {
|
||||
addSubview(self.topView)
|
||||
// addSubview(tagsChooseView)
|
||||
// TopHeaderManager.shared.addFromSuperView(self)
|
||||
addSubview(tagsView)
|
||||
addSubview(collectionView)
|
||||
|
||||
topView.snp.makeConstraints { make in
|
||||
make.left.right.top.equalToSuperview()
|
||||
make.height.equalTo(UIDevice().navHeight)
|
||||
}
|
||||
|
||||
tagsView.snp.makeConstraints { make in
|
||||
make.right.left.equalToSuperview()
|
||||
make.top.equalTo(topView.snp.bottom).offset(0)
|
||||
make.top.equalTo(TopHeaderManager.shared.buildAndAttach(to: self).snp.bottom).offset(0)
|
||||
}
|
||||
|
||||
collectionView.snp.makeConstraints { make in
|
||||
|
|
@ -206,7 +177,7 @@ extension RolesRootPageView: UICollectionViewDelegate, UICollectionViewDataSourc
|
|||
let maxHeight = lineHeight * CGFloat(maxLines)
|
||||
|
||||
// 真实文本高度(不会超过 maxHeight)
|
||||
let textSize = model.name.boundingRect(
|
||||
let textSize = (model.name ?? "").boundingRect(
|
||||
with: CGSize(width: itemWidth - 20, height: maxHeight), // 高度封顶
|
||||
options: [.usesLineFragmentOrigin, .usesFontLeading],
|
||||
attributes: [.font: font],
|
||||
|
|
@ -216,8 +187,7 @@ extension RolesRootPageView: UICollectionViewDelegate, UICollectionViewDataSourc
|
|||
let textH = min(textSize.height, maxHeight)
|
||||
|
||||
// 防止 remind 数组越界,使用模运算或默认值
|
||||
let remindIndex = indexPath.item % remind.count
|
||||
let remindText = remind[remindIndex]
|
||||
let remindText = model.sourceName ?? ""
|
||||
let remindH = remindText.boundingRect(
|
||||
with: CGSize(width: itemWidth - 20.0, height: .greatestFiniteMagnitude),
|
||||
options: .usesLineFragmentOrigin,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// DoubleExt.swift
|
||||
// Visual_Novel_iOS
|
||||
//
|
||||
// Created by mh on 2025/11/11.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Double {
|
||||
/// 截断保留小数位数(向下取整)
|
||||
func truncate(places: Int) -> Double {
|
||||
let divisor = pow(10.0, Double(places))
|
||||
return floor(self * divisor) / divisor
|
||||
}
|
||||
|
||||
/// 截断保留小数位数并格式化为字符串
|
||||
func truncateString(places: Int) -> String {
|
||||
let truncated = truncate(places: places)
|
||||
return String(format: "%.\(places)f", truncated)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue