165 lines
6.3 KiB
Swift
165 lines
6.3 KiB
Swift
//
|
||
// 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.isHidden = true
|
||
return
|
||
|
||
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.contentMode = .scaleAspectFit
|
||
heroImageView.image = UIImage(named: "SearchLocation/header_bg")
|
||
heroImageView.layoutChain
|
||
.top()
|
||
.centerX()
|
||
.width(288)
|
||
.height(190)
|
||
|
||
let count = Int.random(in: 8000...15000)
|
||
let formatter = NumberFormatter()
|
||
formatter.numberStyle = .decimal
|
||
formatter.groupingSeparator = ","
|
||
let countText = formatter.string(from: NSNumber(value: count)) ?? "\(count)"
|
||
usageLab.text = "有\(countText)人正在使用此功能"
|
||
usageLab.font = .systemFont(ofSize: 14, weight: .medium)
|
||
usageLab.textColor = UIColor(hexStr: "#999999")
|
||
usageLab.textAlignment = .center
|
||
usageLab.layoutChain
|
||
.topToBottomOfView(heroImageView, offset: 42)
|
||
.centerX()
|
||
|
||
headlineLab.text = "实时查看TA的位置"
|
||
headlineLab.font = FontManager.boboBold(30)
|
||
headlineLab.textColor = UIColor(hexStr: "#293445")
|
||
headlineLab.textAlignment = .center
|
||
headlineLab.layoutChain
|
||
.topToBottomOfView(usageLab, offset: 37)
|
||
.centerX()
|
||
|
||
subheadLab.text = "为你安心守护出行安全"
|
||
subheadLab.font = .systemFont(ofSize: 18, weight: .bold)
|
||
subheadLab.textColor = UIColor(hexStr: "#293445")
|
||
subheadLab.textAlignment = .center
|
||
subheadLab.layoutChain
|
||
.topToBottomOfView(headlineLab, offset: 18)
|
||
.centerX()
|
||
.bottom()
|
||
|
||
marqueeScrollView.isHidden = true
|
||
marqueeScrollView.backgroundColor = .clear
|
||
marqueeScrollView.showsHorizontalScrollIndicator = false
|
||
marqueeScrollView.isScrollEnabled = false
|
||
}
|
||
|
||
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)
|
||
}
|
||
}
|
||
}
|