角色list

This commit is contained in:
mh 2025-11-11 14:42:47 +08:00
parent 2c6bb330af
commit 8f61a1e969
9 changed files with 115 additions and 88 deletions

View File

@ -18,42 +18,58 @@ final class TopHeaderManager {
return view return view
}() }()
private var hostingView: UIView? // CLTopHeaderView
private var topConstraint: Constraint? // CLTopHeaderView
private var heightConstraint: Constraint? @discardableResult
func buildAndAttach(to superView: UIView) -> CLTopHeaderView {
private init() {} if let existing = superView.subviews.first(where: { $0 is CLTopHeaderView }) as? CLTopHeaderView {
return existing
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 let header = CLTopHeaderView()
headerView.isHidden = false superView.addSubview(header)
header.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
make.height.equalTo(UIDevice().navHeight)
}
return header
} }
func show() { // private var hostingView: UIView?
headerView.isHidden = false // private var topConstraint: Constraint?
} // private var heightConstraint: Constraint?
//
func hide() { // private init() {}
headerView.isHidden = true //
} // var jumpPublisher: AnyPublisher<JumpTarget, Never> {
// headerView.jumpPublisher
func detach() { // }
headerView.removeFromSuperview() //
hostingView = nil // 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
// }
} }

View File

@ -27,17 +27,27 @@ struct RoleListModel: Codable {
var rows: [RoleItem] = [] var rows: [RoleItem] = []
} }
enum RoleSourceType: Int, Codable {
case novel
case video
case other
}
struct RoleItem: Codable { struct RoleItem: Codable {
var id: String = "" var id: String?
var name: String = "" var name: String?
var coverImage: String = "" var coverImage: String?
//
var sourceCoverImage: String?
var sourceName: String?
// //
var headPortrait: String = "" var headPortrait: String?
var score: Double? var score: Double?
var description: String = "" var description: String?
var updateTime: String = "" var updateTime: String?
var sourceId: String = "" var sourceId: String?
var sourceType: Int = 0 // 0:;1:;2:
var sourceType: RoleSourceType?
var commonCount: Int? var commonCount: Int?
var tags: [tagItem] = [] var tags: [tagItem] = []
} }

View File

@ -28,6 +28,9 @@ class FriendsRootHomeController: CLTabRootController<FriendsRootHomeView> {
// view.showEmpty(text: "Friends Coming soon") // view.showEmpty(text: "Friends Coming soon")
// Tab Header
_ = TopHeaderManager.shared.buildAndAttach(to: view)
setupViews() setupViews()
setupDats() setupDats()
setupEvents() setupEvents()

View File

@ -23,6 +23,8 @@ class HomePageRootController: CLTabRootController<HomePageRootView> {
//container.viewModel = viewModel //container.viewModel = viewModel
container.setupViewModel(vm: viewModel) container.setupViewModel(vm: viewModel)
// Tab Header
_ = TopHeaderManager.shared.buildAndAttach(to: view)
// Do any additional setup after loading the view. // Do any additional setup after loading the view.
// view.showEmpty(text: " Coming soon") // view.showEmpty(text: " Coming soon")

View File

@ -34,6 +34,7 @@ class HomePageRootView: UIView {
} }
private func setupViews() { private func setupViews() {
// TopHeaderManager.shared.addFromSuperView(self)
emptyView = { emptyView = {
let v = UIView() let v = UIView()

View File

@ -17,6 +17,9 @@ class MeRootPageController: CLTabRootController<MeRootPageView> {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// Tab Header
_ = TopHeaderManager.shared.buildAndAttach(to: view)
setupViews() setupViews()
setupDats() setupDats()
setupEvents() setupEvents()

View File

@ -20,7 +20,6 @@ class CLRoleCollectionCell: UICollectionViewCell {
lazy var bookImgView: UIImageView = { lazy var bookImgView: UIImageView = {
let imgView = UIImageView() let imgView = UIImageView()
imgView.backgroundColor = .blue
imgView.cornerRadius = 7 imgView.cornerRadius = 7
return imgView return imgView
}() }()
@ -135,12 +134,13 @@ class CLRoleCollectionCell: UICollectionViewCell {
func setupData(item: RoleItem) { func setupData(item: RoleItem) {
descLab.text = item.description descLab.text = item.description
nameLab.text = item.name nameLab.text = item.name
coverImgView.sd_setImage(with: URL(string: item.coverImage), placeholderImage: nil) coverImgView.sd_setImage(with: URL(string: item.coverImage ?? ""), placeholderImage: nil)
tagLab.text = item.tags.compactMap { $0.name }.joined(separator: "/") bookImgView.sd_setImage(with: URL(string: item.sourceCoverImage ?? ""), placeholderImage: nil)
// 0:;1:;2: tagLab.text = item.tags.compactMap { "#\($0.name)" }.joined(separator: "/")
self.bookBgImgView.isHidden = item.sourceType != 0 sourceLab.text = item.score?.truncateString(places: 1)
self.playImgView.isHidden = item.sourceType != 1 self.bookBgImgView.isHidden = item.sourceType != .novel
// coverImgView. self.playImgView.isHidden = item.sourceType != .video
self.remindLab.text = item.sourceName
} }
// MARK: subviews // MARK: subviews

View File

@ -13,7 +13,7 @@ import Combine
class RolesRootPageView: CLContainer { class RolesRootPageView: CLContainer {
let itemWidth: CGFloat = (UIScreen.width - 30.0) / 2.0 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) // private lazy var pagingView = JXPagingListRefreshView(delegate: self)
// lazy var headerView: // lazy var headerView:
@ -25,33 +25,10 @@ class RolesRootPageView: CLContainer {
var data: [RoleItem] = [] var data: [RoleItem] = []
var hasMoreData: Bool = true // var hasMoreData: Bool = true //
let remind: [String] = [ // lazy var topView: CLTopHeaderView = {
"[The Lsat Oracle of Kael]", // let view = CLTopHeaderView()
"[The Lsat Oracle of Kael]", // return view
"[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 tagsView: CLRoleTagsView = { lazy var tagsView: CLRoleTagsView = {
let tagsView = CLRoleTagsView() let tagsView = CLRoleTagsView()
@ -85,19 +62,13 @@ class RolesRootPageView: CLContainer {
} }
private func setupViews() { private func setupViews() {
addSubview(self.topView) // TopHeaderManager.shared.addFromSuperView(self)
// addSubview(tagsChooseView)
addSubview(tagsView) addSubview(tagsView)
addSubview(collectionView) addSubview(collectionView)
topView.snp.makeConstraints { make in
make.left.right.top.equalToSuperview()
make.height.equalTo(UIDevice().navHeight)
}
tagsView.snp.makeConstraints { make in tagsView.snp.makeConstraints { make in
make.right.left.equalToSuperview() 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 collectionView.snp.makeConstraints { make in
@ -206,7 +177,7 @@ extension RolesRootPageView: UICollectionViewDelegate, UICollectionViewDataSourc
let maxHeight = lineHeight * CGFloat(maxLines) let maxHeight = lineHeight * CGFloat(maxLines)
// maxHeight // maxHeight
let textSize = model.name.boundingRect( let textSize = (model.name ?? "").boundingRect(
with: CGSize(width: itemWidth - 20, height: maxHeight), // with: CGSize(width: itemWidth - 20, height: maxHeight), //
options: [.usesLineFragmentOrigin, .usesFontLeading], options: [.usesLineFragmentOrigin, .usesFontLeading],
attributes: [.font: font], attributes: [.font: font],
@ -216,8 +187,7 @@ extension RolesRootPageView: UICollectionViewDelegate, UICollectionViewDataSourc
let textH = min(textSize.height, maxHeight) let textH = min(textSize.height, maxHeight)
// remind 使 // remind 使
let remindIndex = indexPath.item % remind.count let remindText = model.sourceName ?? ""
let remindText = remind[remindIndex]
let remindH = remindText.boundingRect( let remindH = remindText.boundingRect(
with: CGSize(width: itemWidth - 20.0, height: .greatestFiniteMagnitude), with: CGSize(width: itemWidth - 20.0, height: .greatestFiniteMagnitude),
options: .usesLineFragmentOrigin, options: .usesLineFragmentOrigin,

View File

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