// // EncounterAICardView.swift // Crush // // Created by Leon on 2025/9/10. // import UIKit import Combine protocol EncounterAICardViewDelegate: AnyObject{ func cardViewTapLike(_ obj: MeetCard?) func cardViewTapGift(_ obj: MeetCard?) func cardViewTapDislike(_ obj: MeetCard?) func cardViewTapHeader(_ obj: MeetCard?, cardView: EncounterAICardView?) } class EncounterAICardView: MeetDragCardView { static let cardWidth = UIScreen.width - 12 * 2.0 static let cardHeight = UIScreen.height - UIWindow.tabBarTotalHeight - UIWindow.navBarTotalHeight - 12 - 12 var scrollContainer: LTScrollContainer! var indicator: PageIndicatorView! var header: EncounterAICardHeader! var albumView: EncounterAICardAlbumView! weak var delegate : EncounterAICardViewDelegate? var card: MeetCard? // Flag var isPullingToAIHomePage = false private var cancellables = Set() override init(frame: CGRect) { super.init(frame: frame) setupViews() setupData() setupEvent() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupViews() { backgroundColor = .c.cbd scrollContainer = { let v = LTScrollContainer() v.scrollView.delegate = self v.scrollView.bounces = true v.scrollView.showsVerticalScrollIndicator = false addSubview(v) v.snp.makeConstraints { make in make.edges.equalToSuperview() } return v }() indicator = { let v = PageIndicatorView() addSubview(v) v.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview().offset(-4) } return v }() header = { let v = EncounterAICardHeader() scrollContainer.stack.addArrangedSubview(v) v.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() } return v }() albumView = { let v = EncounterAICardAlbumView() scrollContainer.stack.addArrangedSubview(v) v.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() } return v }() } private func setupData(){ } private func setupEvent(){ header.dislikeButton.addTarget(self, action: #selector(tapDislikeButton), for: .touchUpInside) header.likeButton.addTarget(self, action: #selector(tapLikeButton), for: .touchUpInside) header.giftButton.addTarget(self, action: #selector(tapGiftButton), for: .touchUpInside) header.imageBgButton.addTarget(self, action: #selector(tapImageBgButton), for: .touchUpInside) header.overlayBgButton.addTarget(self, action: #selector(tapImageBgButton), for: .touchUpInside) PhotosViewModel.shared.$album.sink {[weak self] photo in guard let self = self else{return} guard let album = photo , let albumId = photo?.albumId else {return} let albumList = self.card?.albumList ?? [] for per in albumList{ let perAlbum = per if albumId == perAlbum.albumId{ perAlbum.updateFrom(album) self.albumView.config(albumList) break } } }.store(in: &cancellables) } // MARK: - Public func config(_ data: MeetCard?){ self.card = data guard let card = data else { return } // 配置Header数据 header.config(card) // 配置相册数据 albumView.configCard(card: card) albumView.config(card.albumList ?? []) } // MARK: - Action @objc func tapDislikeButton(){ delegate?.cardViewTapDislike(card) } @objc func tapLikeButton(){ delegate?.cardViewTapLike(card) } @objc func tapGiftButton(){ delegate?.cardViewTapGift(card) } @objc func tapImageBgButton(){ delegate?.cardViewTapHeader(card, cardView: self) } } extension EncounterAICardView: UIScrollViewDelegate{ func scrollViewDidScroll(_ scrollView: UIScrollView) { // 计算滚动进度 (0.0 到 1.0) let maxOffset = scrollView.contentSize.height - scrollView.bounds.size.height let progress = maxOffset > 0 ? scrollView.contentOffset.y / maxOffset : 0 //dlog("progress:\(progress)") // 更新指示器 //indicator.updateIndicator(progress: progress) indicator.updateIndicator(top: progress >= 0.5) } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { let offsetY = scrollView.contentOffset.y let contentHeight = scrollView.contentSize.height let height = scrollView.frame.size.height if offsetY > contentHeight - height + 40 { guard let aiId = card?.aiId else{return} //print("松手时,滑到底部之外了") if isPullingToAIHomePage == false{ CLTool.feedbackGenerator() AppRouter.goAIRoleHome(aiId: aiId) isPullingToAIHomePage = true } DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {[weak self] in self?.isPullingToAIHomePage = false } } else if offsetY < 0 { //print("松手时,滑到顶部之外了") } } }