// // SPEpisodeView.swift // Thimra // // Created by 曾觉新 on 2025/4/16. // import UIKit class SPEpisodeView: HWPanModalContentView { var currentIndex: Int = 0 { didSet { self.collectionView.reloadData() } } var shortModel: SPShortModel? { didSet { coverImageView.sp_setImage(url: shortModel?.image_url) titleLabel.text = shortModel?.name desLabel.text = shortModel?.sp_description } } var dataArr: [SPVideoInfoModel] = [] { didSet { self.collectionView.reloadData() var menuDataArr = [String]() let totalEpisode = dataArr.count var index = 0 var remainingEpisodes = totalEpisode while remainingEpisodes > 0 { let minIndex = index * 30 var maxIndex = minIndex + 29 if maxIndex >= dataArr.count { maxIndex = dataArr.count - 1 } let minEpisode = dataArr[minIndex].episode ?? "0" let maxEpisode = dataArr[maxIndex].episode ?? "0" if minEpisode == maxEpisode { menuDataArr.append("\(minEpisode)") } else { menuDataArr.append("\(minEpisode)-\(maxEpisode)") } remainingEpisodes -= 30 index += 1 } self.menuView.dataArr = menuDataArr } } var didSelectedIndex: ((_ index: Int) -> Void)? var isDecelerating = false var isDragging = false //MARK: UI属性 private lazy var bgView: UIView = { let view = UIImageView(image: UIImage(named: "episode_bg_image_01")) return view }() private lazy var collectionViewLayout: UICollectionViewFlowLayout = { let itemWidth = floor((kSPScreenWidth - 7 * 4 - 32) / 5) let layout = UICollectionViewFlowLayout() layout.itemSize = .init(width: itemWidth, height: 54) layout.minimumLineSpacing = 7 layout.minimumInteritemSpacing = 7 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 SPEpisodeCell.registerCell(collectionView: collectionView) return collectionView }() private lazy var indicatorView: UIView = { let view = UIView() view.backgroundColor = .color5A5C67() view.layer.cornerRadius = 2.5 view.layer.masksToBounds = true return view }() private lazy var coverImageView: SPImageView = { let imageView = SPImageView() imageView.layer.cornerRadius = 4 imageView.layer.masksToBounds = true imageView.layer.borderColor = UIColor.colorFFFFFF(alpha: 0.26).cgColor imageView.layer.borderWidth = 1 return imageView }() private lazy var titleLabel: UILabel = { let label = UILabel() label.font = .fontBold(ofSize: 14) label.textColor = .colorFFFFFF() label.numberOfLines = 2 return label }() private lazy var desLabel: UILabel = { let label = UILabel() label.font = .fontRegular(ofSize: 10) label.textColor = .colorA8A5AA() label.numberOfLines = 5 return label }() private lazy var lineView: UIView = { let view = UIView() view.backgroundColor = .color545454() return view }() private lazy var menuView: SPEpisodeMenuView = { let view = SPEpisodeMenuView() view.didSelectedIndex = { [weak self] index in guard let self = self else { return } var row = 0 if index > 0 { row = index * 30 + 10 let count = self.dataArr.count if row >= count { row = count - 1 } } let indexPath = IndexPath.init(row: row, section: 0) self.collectionView.scrollToItem(at: indexPath, at: .centeredVertically, animated: true) } return view }() override init(frame: CGRect) { super.init(frame: frame) _setupUI() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } //MARK: HWPanModalPresentable override func panScrollable() -> UIScrollView? { return collectionView } override func longFormHeight() -> PanModalHeight { return PanModalHeightMake(.content, kSPScreenHeight * (2 / 3)) } override func showDragIndicator() -> Bool { return false } override func backgroundConfig() -> HWBackgroundConfig { let config = HWBackgroundConfig() config.backgroundAlpha = 0.4 return config } // override func present(in view: UIView?) { // super.present(in: view) // self.hw_contentView.addEffectView(style: .dark) // } } extension SPEpisodeView { private func _setupUI() { addSubview(bgView) addSubview(indicatorView) addSubview(coverImageView) addSubview(titleLabel) addSubview(desLabel) addSubview(menuView) addSubview(lineView) addSubview(collectionView) bgView.snp.makeConstraints { make in make.edges.equalToSuperview() } self.indicatorView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalToSuperview().offset(10) make.width.equalTo(40) make.height.equalTo(5) } self.coverImageView.snp.makeConstraints { make in make.left.equalToSuperview().offset(15) make.top.equalToSuperview().offset(39) make.width.equalTo(70) make.height.equalTo(104) } self.titleLabel.snp.makeConstraints { make in make.left.equalTo(self.coverImageView.snp.right).offset(12) make.right.lessThanOrEqualToSuperview().offset(-16) make.top.equalTo(self.coverImageView) } self.desLabel.snp.makeConstraints { make in make.left.equalTo(self.titleLabel) make.right.lessThanOrEqualToSuperview().offset(-16) // make.top.equalTo(self.coverImageView.snp.bottom).offset(8) make.top.equalTo(titleLabel.snp.bottom).offset(6) } self.menuView.snp.makeConstraints { make in make.left.right.equalTo(self.lineView) make.bottom.equalTo(self.lineView) } self.lineView.snp.makeConstraints { make in make.left.equalToSuperview().offset(16) make.centerX.equalToSuperview() make.top.equalTo(self.coverImageView.snp.bottom).offset(46) make.height.equalTo(0.7) } self.collectionView.snp.makeConstraints { make in // make.edges.equalToSuperview() make.left.right.bottom.equalToSuperview() make.top.equalTo(self.lineView.snp.bottom).offset(15) } } } //MARK: -------------- UICollectionViewDelegate & UICollectionViewDataSource -------------- extension SPEpisodeView: UICollectionViewDelegate, UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = SPEpisodeCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath) cell.videoInfoModel = self.dataArr[indexPath.row] cell.sp_isSelected = indexPath.row == currentIndex return cell } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.dataArr.count } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard indexPath.row != currentIndex else { return } self.didSelectedIndex?(indexPath.row) self.dismiss(animated: true) { } } func scrollViewDidScroll(_ scrollView: UIScrollView) { if isDragging || isDecelerating { updateMuneSelectedIndex() } } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { isDecelerating = false updateMuneSelectedIndex() } func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { isDecelerating = true } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { isDragging = true } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { isDragging = false } func updateMuneSelectedIndex() { let indexPathArr = collectionView.indexPathsForVisibleItems var minRow = dataArr.count - 1 var maxRow = 0 for indexPath in indexPathArr { if indexPath.row < minRow { minRow = indexPath.row } if indexPath.row > maxRow { maxRow = indexPath.row } } let selectedIndex = maxRow / 30 if menuView.selectedIndex != selectedIndex { menuView.selectedIndex = selectedIndex } } }