// // SearchLocationHeaderView.swift // QuickLocation // import UIKit /// 查位置页共用顶部:hero + 使用人数 + 双标题 + 跑马灯 final class SearchLocationHeaderView: UIView { let usageLab = UILabel() let headlineLab = UILabel() let subheadLab = UILabel() let marqueeScrollView = UIScrollView() /// 猫插画 + 装饰合图;有切图时设 image 并清空 backgroundColor let heroImageView = UIImageView() override init(frame: CGRect) { super.init(frame: frame) setupUI() setupMarquee() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func startMarqueeAnimation() { marqueeScrollView.layer.removeAllAnimations() marqueeScrollView.contentOffset.x = 0 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in guard let self = self else { return } let contentW = self.marqueeScrollView.contentSize.width guard contentW > self.marqueeScrollView.bounds.width else { return } let duration = contentW / 100 UIView.animate(withDuration: duration, delay: 0, options: [.curveLinear, .repeat]) { self.marqueeScrollView.contentOffset.x = contentW - self.marqueeScrollView.bounds.width } } } private func setupUI() { addSubview(heroImageView) addSubview(usageLab) addSubview(headlineLab) addSubview(subheadLab) addSubview(marqueeScrollView) heroImageView.backgroundColor = .clear heroImageView.image = UIImage(named: "SearchLocation/header_bg") heroImageView.layoutChain .top() .centerX() .width(288) .height(190) let count = Int.random(in: 8000...15000) let usageText = "有 \(count) 人正在使用此功能" let attr = NSMutableAttributedString(string: usageText) if let range = usageText.range(of: "\(count)") { attr.addAttribute(.foregroundColor, value: UIColor(hexStr: "#16B3FF"), range: NSRange(range, in: usageText)) } usageLab.attributedText = attr usageLab.font = .systemFont(ofSize: 14, weight: .medium) usageLab.textColor = UIColor(hexStr: "#999999") usageLab.textAlignment = .center usageLab.layoutChain .topToBottomOfView(heroImageView, offset: 8) .centerX() headlineLab.text = "实时查看TA的位置" headlineLab.font = FontManager.boboBold(30) headlineLab.textColor = UIColor(hexStr: "#293445") headlineLab.textAlignment = .center headlineLab.layoutChain .topToBottomOfView(usageLab, offset: 16) .centerX() subheadLab.text = "为你安心守护出行安全" subheadLab.font = .systemFont(ofSize: 18, weight: .bold) subheadLab.textColor = UIColor(hexStr: "#293445") subheadLab.textAlignment = .center subheadLab.layoutChain .topToBottomOfView(headlineLab, offset: 6) .centerX() marqueeScrollView.backgroundColor = .clear marqueeScrollView.showsHorizontalScrollIndicator = false marqueeScrollView.isScrollEnabled = false marqueeScrollView.layoutChain .topToBottomOfView(subheadLab, offset: 12) .edgesHorzontal() .height(36) .bottom() } private func randomPhoneNumber() -> String { let prefixes = [ "134","135","136","137","138","139","147","150","151","152","157","158","159","178","182","183","184","187","188","198", "130","131","132","145","155","156","166","175","176","185","186","196", "133","149","153","173","177","180","181","189","191","199" ] let prefix = prefixes.randomElement()! var suffix = "" for _ in 0..<8 { suffix.append("\(Int.random(in: 0...9))") } return prefix + suffix } private func setupMarquee() { let surnames = ["张", "李", "王", "陈", "刘", "杨", "赵", "黄", "周", "吴", "林", "何", "马", "胡", "郑", "梁", "谢", "宋", "唐", "韩"] let container = UIView() container.backgroundColor = .clear marqueeScrollView.addSubview(container) var prevView: UIView? for _ in 0..<10 { let iconIndex = Int.random(in: 1...15) let surname = surnames.randomElement() ?? "张" let maskedName = "\(surname)**" let phone = randomPhoneNumber() let maskedPhone = phone.prefix(3) + "******" + phone.suffix(2) let itemView = UIView() itemView.backgroundColor = UIColor(hexStr: "#EFF9FF") itemView.cornerRadius = 8 container.addSubview(itemView) let avatar = UIImageView(image: UIImage(named: "UserIcon/\(iconIndex)")) avatar.contentMode = .scaleAspectFill avatar.backgroundColor = UIColor(hexStr: "#E0E0E0") avatar.cornerRadius = 12 avatar.clipsToBounds = true itemView.addSubview(avatar) let label = UILabel() label.font = .systemFont(ofSize: 13, weight: .medium) let fullText = "\(maskedName) 定位到了 \(maskedPhone)" let attr = NSMutableAttributedString(string: fullText) attr.addAttribute(.foregroundColor, value: UIColor(hexStr: "#16B3FF"), range: NSRange(fullText.range(of: maskedName)!, in: fullText)) if let phoneRange = fullText.range(of: maskedPhone) { attr.addAttribute(.foregroundColor, value: UIColor(hexStr: "#16B3FF"), range: NSRange(phoneRange, in: fullText)) } label.attributedText = attr itemView.addSubview(label) avatar.layoutChain.left(10).centerY().width(24).height(24) label.layoutChain.leftToRightOfView(avatar, offset: 6).centerY().right(10) itemView.layoutChain.centerY().height(30) if let prev = prevView { itemView.layoutChain.leftToRightOfView(prev, offset: 16) } else { itemView.layoutChain.left() } prevView = itemView } if let last = prevView { container.layoutChain.edges().heightToView(marqueeScrollView).rightToView(last) } } }