// // EncounterAICardHeader.swift // Crush // // Created by Leon on 2025/9/11. // class EncounterAICardHeader:UIView{ var imageFieldContainer: UIView! var imageBgButton: UIButton! var imageView: UIImageView! var topRightTag: EPTagLabel! var overlay: GradientView! var overlayBgButton: UIButton! var bottomButtonsStackH : UIStackView! var dislikeButton: UIButton! var giftButton : EPIconPrimaryButton! var likeButton : UIButton! var likeAndCount: CLIconLabel! var nameLabel: CLLabel! var tagsStackH: UIStackView! var introductionStackH: UIStackView! var introductionLabel: LineSpaceLabel! var introductionLabelTopButton: UIButton! var expendButton: EPIconGhostButton! var aiGeneratedLabel: CLLabel! var albumTitleView: UIView! var albumTitleLabel : CLLabel! var data: MeetCard? override init(frame: CGRect) { super.init(frame: frame) setupViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupViews() { imageFieldContainer = { let v = UIView() v.backgroundColor = .clear v.clipsToBounds = true addSubview(v) v.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.height.equalTo(EncounterAICardView.cardHeight) } return v }() imageBgButton = { let v = UIButton() imageFieldContainer.addSubview(v) v.snp.makeConstraints { make in make.edges.equalToSuperview() } return v }() imageView = { let v = UIImageView() v.contentMode = .scaleAspectFill imageFieldContainer.addSubview(v) v.snp.makeConstraints { make in make.edges.equalToSuperview() } return v }() topRightTag = { let v = EPTagLabel(style: .primary, size: .large) imageFieldContainer.addSubview(v) v.snp.makeConstraints { make in make.top.equalToSuperview().offset(24) make.trailing.equalToSuperview().offset(-16) } return v }() overlay = { let color = UIColor.c.cbd let v = GradientView(colors: [color.withAlphaComponent(0), color.withAlphaComponent(0.6), color.withAlphaComponent(0.9)], gradientType: .topToBottom) imageFieldContainer.addSubview(v) v.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() make.bottom.equalToSuperview() make.height.equalTo(368) } return v }() bottomButtonsStackH = { let v = UIStackView() v.spacing = 32 v.alignment = .center imageFieldContainer.addSubview(v) v.snp.makeConstraints { make in make.bottom.equalToSuperview().offset(-40) make.centerX.equalToSuperview() } return v }() dislikeButton = { let v = UIButton() let image = UIImage(named: "encounter_icon_dislike") v.setImage(image, for: .normal) v.backgroundColor = .c.cseln v.cornerRadius = 32 v.contentMode = .center bottomButtonsStackH.addArrangedSubview(v) v.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 64, height: 64)) } return v }() giftButton = { let v = EPIconPrimaryButton(radius: .round, iconSize: .large, iconCode: .giftBorder) bottomButtonsStackH.addArrangedSubview(v) v.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 48, height: 48)) } return v }() likeButton = { let v = UIButton() let image = UIImage(named: "encounter_icon_like") v.setImage(image, for: .normal) v.backgroundColor = .c.cseln v.cornerRadius = 32 v.contentMode = .center bottomButtonsStackH.addArrangedSubview(v) v.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 64, height: 64)) } return v }() setupMiddlePart() setupAlbumTitlePart() overlayBgButton = { let v = UIButton() overlay.insertSubview(v, at: 0) v.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.bottom.equalTo(introductionStackH.snp.top).offset(-8) } return v }() // #warning("test") // testData() } private func setupMiddlePart(){ nameLabel = { let v = CLLabel() v.numberOfLines = 1 // 暂时限制1行 v.font = .t.thl overlay.addSubview(v) v.snp.makeConstraints { make in make.leading.equalToSuperview().offset(24) make.trailing.equalToSuperview().offset(-96) make.bottom.equalTo(overlay.snp.top).offset(112) } return v }() likeAndCount = { let v = CLIconLabel() v.iconSize = CGSize(width: 20, height: 20) v.contentLabel.font = .t.tnds v.iconImageView.image = MWIconFont.image(fromIcon: .like, size: CGSize(width: 20, height: 20), color: .white) overlay.addSubview(v) v.snp.makeConstraints { make in make.trailing.equalToSuperview().offset(-24) make.centerY.equalTo(nameLabel) } return v }() tagsStackH = { let v = UIStackView() v.spacing = 8 overlay.addSubview(v) v.snp.makeConstraints { make in make.leading.equalToSuperview().offset(24) make.top.equalTo(nameLabel.snp.bottom).offset(8) } return v }() introductionStackH = { let v = UIStackView() v.spacing = 0 v.alignment = .bottom overlay.addSubview(v) v.snp.makeConstraints { make in make.leading.equalToSuperview().offset(24) make.trailing.equalToSuperview().offset(-24) make.top.equalTo(tagsStackH.snp.bottom).offset(8) } return v }() introductionLabel = { let v = LineSpaceLabel() let typography = CLSystemToken.typography(token: .tbm) v.textColor = .text v.config(typography) v.numberOfLines = 3 introductionStackH.addArrangedSubview(v) return v }() introductionLabelTopButton = { let v = UIButton() v.addTarget(self, action: #selector(tapIntroductionTop), for: .touchUpInside) overlay.addSubview(v) v.snp.makeConstraints { make in make.edges.equalTo(introductionLabel) } return v }() expendButton = { let v = EPIconGhostButton(radius: .none, iconSize: .xs, iconCode: .iconFullimage) v.addTarget(self, action: #selector(tapExpandButton), for: .touchUpInside) introductionStackH.addArrangedSubview(v) v.snp.makeConstraints { make in make.size.equalTo(v.bgImageSize()) } v.isHidden = true // 默认隐藏 return v }() aiGeneratedLabel = { let v = CLLabel() v.textColor = .c.ctsn v.font = .t.tbs overlay.addSubview(v) v.snp.makeConstraints { make in make.leading.equalToSuperview().offset(24) make.trailing.equalToSuperview().offset(-24) make.top.equalTo(introductionStackH.snp.bottom).offset(8) } return v }() } private func setupAlbumTitlePart(){ albumTitleView = { let v = UIView() addSubview(v) v.snp.makeConstraints { make in make.leading.equalToSuperview().offset(CGFloat.lrs) make.trailing.equalToSuperview().offset(-CGFloat.lrs) make.top.equalTo(imageFieldContainer.snp.bottom).offset(16) make.bottom.equalToSuperview().offset(-24) make.height.equalTo(32) } return v }() albumTitleLabel = { let v = CLLabel() v.font = .t.ttm albumTitleView.addSubview(v) v.snp.makeConstraints { make in make.center.equalToSuperview() } return v }() do{ let left = { let v = CLLine() albumTitleView.addSubview(v) v.snp.makeConstraints { make in make.height.equalTo(1) make.leading.equalToSuperview() make.trailing.equalTo(albumTitleLabel.snp.leading).offset(-16) make.centerY.equalTo(albumTitleLabel) } return v }() let right = { let v = CLLine() albumTitleView.addSubview(v) v.snp.makeConstraints { make in make.height.equalTo(1) make.trailing.equalToSuperview() make.leading.equalTo(albumTitleLabel.snp.trailing).offset(16) make.centerY.equalTo(albumTitleLabel) } return v }() left.isHidden = false right.isHidden = false } albumTitleLabel.text = "Album" topRightTag.text = "Secret Admirer" topRightTag.isHidden = true } private func testData(){ nameLabel.text = "Alice, 24" likeAndCount.contentLabel.text = "1.2k" do { let tag = RoleTag() tag.title = "Sensibility" tag.style = .blurPurple tagsStackH.addArrangedSubview(tag) } do { let tag = RoleTag() tag.title = "Romantic" tag.style = .blurTheme tagsStackH.addArrangedSubview(tag) } introductionLabel.text = "She is a new trainee teacher who has just graduated. You are the most rebellious student in the whole school. In order to prove her ability, she took the initiative to apply to be your class teacher. In the upcoming college entrance examination, you two…" aiGeneratedLabel.text = "Content generated by AI" imageView.image = UIImage.egpic } // MARK: - Public Methods func config(_ card: MeetCard) { data = card // 配置头像 if let imageUrl = card.imageUrl { imageView.loadImage(imageUrl) } // 配置右上角标签 if data?.secretAdmirer ?? false{ topRightTag.isHidden = false }else { topRightTag.isHidden = true } // 配置姓名和年龄 let name = card.nickname ?? "Unknown" let age = card.age != nil ? ", \(card.age!)" : "" nameLabel.text = "\(name)\(age)" // 配置点赞数 let likeCount = card.likedCount ?? 0 likeAndCount.contentLabel.text = formatLikeCount(likeCount) // 清除现有标签 tagsStackH.arrangedSubviews.forEach { $0.removeFromSuperview() } // 配置性格标签 if let character = card.character, !character.isEmpty { let tag = RoleTag() tag.title = character //tag.style = .blurTheme tag.style = .default tagsStackH.addArrangedSubview(tag) } // 配置标签 if let tag = card.tag, !tag.isEmpty { let tagView = RoleTag() tagView.title = tag //tagView.style = .blurPurple tagView.style = .default tagsStackH.addArrangedSubview(tagView) } // 配置简介 introductionLabel.text = card.introduction ?? "" updateExpandButtonVisibility() // 添加这行来更新按钮显示状态 aiGeneratedLabel.text = "Content generated by AI" } private func formatLikeCount(_ count: Int) -> String { if count >= 1000 { return String(format: "%.1fk", Double(count) / 1000.0) } else { return "\(count)" } } private func updateExpandButtonVisibility() { guard let text = introductionLabel.text, !text.isEmpty else { expendButton.isHidden = true return } // 计算文本在3行限制下的实际高度 let maxWidth = introductionLabel.frame.width > 0 ? introductionLabel.frame.width : (UIScreen.main.bounds.width - 48) // 减去左右边距 let maxSize = CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude) // 计算3行文本的高度 let threeLineHeight = introductionLabel.font.lineHeight * 3 + introductionLabel.font.lineHeight * 2 // 3行 + 2个行间距 // 计算实际文本需要的高度 let actualSize = text.boundingRect( with: maxSize, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: introductionLabel.font!], context: nil ) // 如果实际高度超过3行高度,显示展开按钮 let hideBrowseIntroduction = actualSize.height <= threeLineHeight expendButton.isHidden = hideBrowseIntroduction introductionLabelTopButton.isHidden = hideBrowseIntroduction } // 重写layoutSubviews来在布局完成后更新按钮状态 override func layoutSubviews() { super.layoutSubviews() updateExpandButtonVisibility() } // MARK: - Action @objc private func tapExpandButton(){ guard let introduction = data?.introduction else{return} let vc = RoleIntroBrowseDialogController() vc.introduction = introduction vc.imageUrl = data?.imageUrl vc.show() } @objc private func tapIntroductionTop(){ tapExpandButton() } }