// // MineView.swift // QuickLocation // // Created based on Lanhu design: 我的·版本6 // import UIKit import RxSwift import RxCocoa final class MineView: UIView { var disposeBag = DisposeBag() // MARK: - Design Constants (iOS points, from Lanhu 版本6 @2x) private let headerHeight: CGFloat = 160 private let avatarSize: CGFloat = 64 private let editBtnSize: CGFloat = 30 private let copyIconSize: CGFloat = 14 private let vipCardHeight: CGFloat = 90 private let settingsRowHeight: CGFloat = 52 // MARK: - Colors private let titleColor = UIColor(hexStr: "#1A1A1A") private let subtitleColor = UIColor(hexStr: "#767676") private let vipBtnTextColor = UIColor(hexStr: "#00343B") private func setupRx() { avatarImageView.rx.tapGesture.subscribe { _ in AppRouter.push(Route.login) }.disposed(by: disposeBag) } // MARK: - UI Components private lazy var headerBgImageView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "Mine/header_bg") iv.contentMode = .scaleAspectFill iv.clipsToBounds = true return iv }() lazy var avatarImageView: UIImageView = { let iv = UIImageView() iv.isUserInteractionEnabled = true iv.contentMode = .scaleAspectFill iv.layer.cornerRadius = avatarSize / 2 iv.layer.borderWidth = 2 iv.layer.borderColor = UIColor.white.cgColor iv.clipsToBounds = true iv.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor iv.layer.shadowOffset = .zero iv.layer.shadowRadius = 2 iv.layer.shadowOpacity = 1 iv.backgroundColor = UIColor(hexStr: "#E0E0E0") iv.image = UIImage(named: "Mine/default_avatar") return iv }() lazy var nameLabel: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 16, weight: .heavy) label.textColor = titleColor label.text = "肖战大王叫我来巡山" return label }() lazy var editButton: UIButton = { let btn = UIButton(type: .custom) btn.setImage(UIImage(named: "Mine/edit"), for: .normal) btn.backgroundColor = .clear return btn }() lazy var idLabel: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 14, weight: .medium) label.textColor = subtitleColor label.text = "ID:1234567" return label }() lazy var copyButton: UIButton = { let btn = UIButton(type: .custom) btn.setImage(UIImage(named: "Mine/copy"), for: .normal) btn.backgroundColor = .clear return btn }() // VIP Card lazy var vipCardView: UIView = { let view = UIView() view.layer.cornerRadius = 16 view.clipsToBounds = true return view }() lazy var vipCardBgImageView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "Mine/vip_card") iv.contentMode = .scaleToFill return iv }() lazy var vipTitleLab: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 18, weight: .bold) label.textColor = .white label.text = "开通会员" return label }() lazy var vipContentLab: UILabel = { let label = UILabel() label.font = .systemFont(ofSize: 12, weight: .medium) label.textColor = .white label.text = "会员尊享10+VIP特权" return label }() lazy var vipActivateButton: UIButton = { let btn = UIButton(type: .custom) btn.backgroundColor = UIColor(hexStr: "#C0FF54") btn.setTitle("立即开通", for: .normal) btn.setTitleColor(UIColor(hexStr: "#00343B"), for: .normal) btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .bold) btn.cornerRadius = 14 return btn }() // Profile transition background (100pt section below VIP card) private lazy var profileBgImageView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "Mine/profile_bg") iv.contentMode = .scaleToFill return iv }() private lazy var bottomFillView: UIView = { let view = UIView() view.backgroundColor = UIColor(hexStr: "#F5FBFB") return view }() // Settings Card private lazy var settingsCardView: UIView = { let view = UIView() view.backgroundColor = .white view.layer.cornerRadius = 12 view.layer.shadowColor = UIColor(red: 0.898, green: 0.898, blue: 0.898, alpha: 0.3).cgColor view.layer.shadowOffset = .zero view.layer.shadowRadius = 4 view.layer.shadowOpacity = 1 return view }() lazy var settingsTableView: UITableView = { let tv = UITableView(frame: .zero, style: .plain) tv.backgroundColor = .clear tv.isScrollEnabled = false tv.separatorStyle = .none tv.register(MineMenuCell.self) tv.rowHeight = settingsRowHeight tv.estimatedRowHeight = settingsRowHeight return tv }() // MARK: - Callbacks var onMenuTap: ((Int) -> Void)? // MARK: - Init override init(frame: CGRect) { super.init(frame: frame) setupUI() setupLayout() setupRx() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Setup private func setupUI() { backgroundColor = .white addSubview(headerBgImageView) addSubview(avatarImageView) addSubview(nameLabel) addSubview(editButton) addSubview(idLabel) addSubview(copyButton) addSubview(vipCardView) vipCardView.addSubview(vipCardBgImageView) vipCardView.addSubview(vipActivateButton) vipCardView.addSubview(vipTitleLab) vipCardView.addSubview(vipContentLab) addSubview(profileBgImageView) addSubview(bottomFillView) addSubview(settingsCardView) settingsCardView.addSubview(settingsTableView) } private func setupLayout() { // Header headerBgImageView.layoutChain .top().left().right() .height(headerHeight) // Avatar avatarImageView.layoutChain .top(kStatusBarHeight + 44) .left(32) .size(CGSize(width: avatarSize, height: avatarSize)) // Name nameLabel.layoutChain .leftToRightOfView(avatarImageView, offset: 20) .topToView(avatarImageView, offset: 8) // Edit button editButton.layoutChain .right(16) .centerY(nameLabel) .size(CGSize(width: editBtnSize, height: editBtnSize)) // ID idLabel.layoutChain .leftToRightOfView(avatarImageView, offset: 20) .topToBottomOfView(nameLabel, offset: 4) // Copy button copyButton.layoutChain .leftToRightOfView(idLabel, offset: 4) .centerY(idLabel) .size(CGSize(width: copyIconSize, height: copyIconSize)) // VIP Card vipCardView.layoutChain .topToBottomOfView(avatarImageView, offset: 28) .left(16).right(16) .height(vipCardHeight) vipCardBgImageView.layoutChain .edges() vipTitleLab.layoutChain .top(22) .left(77) vipContentLab.layoutChain .topToBottomOfView(vipTitleLab, offset: 6) .leftToView(vipTitleLab) vipActivateButton.layoutChain .right(19).centerY() .width(70).height(28) // Profile transition background (100pt, overlaps VIP card bottom half) profileBgImageView.layoutChain .topToView(vipCardView, offset: 32) .left().right() .height(100) // Fill below profile transition bottomFillView.layoutChain .topToBottomOfView(profileBgImageView) .left().right().bottom() // Settings Card (fixed height, not stretched) settingsCardView.layoutChain .topToBottomOfView(vipCardView, offset: 20) .left(16).right(16) .height(312) settingsTableView.layoutChain .edges() } } // MARK: - MineMenuCell final class MineMenuCell: UITableViewCell { func configure(_ model: MineMenuItem) { iconImageView.image = UIImage(named: model.icon) titleLabel.text = model.title } private func setupSubviews() { contentView.addSubview(iconImageView) contentView.addSubview(titleLabel) contentView.addSubview(arrowImageView) contentView.addSubview(dividerLine) iconImageView.layoutChain .left(14).centerY() .size(CGSize(width: 24, height: 24)) titleLabel.layoutChain .leftToRightOfView(iconImageView, offset: 8) .centerY() arrowImageView.layoutChain .right(14).centerY() .size(CGSize(width: 18, height: 18)) dividerLine.layoutChain .left(14).right().bottom() .height(0.5) } var showDivider: Bool = true { didSet { dividerLine.isHidden = !showDivider } } private let iconImageView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFit return iv }() private let titleLabel: UILabel = { let label = UILabel() label.font = UIFont(name: "PingFangSC-Medium", size: 14) ?? UIFont.systemFont(ofSize: 14, weight: .medium) label.textColor = UIColor(hexStr: "#1A1A1A") return label }() private let arrowImageView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFit iv.image = UIImage(named: "Mine/arrow_right") return iv }() private let dividerLine: UIView = { let view = UIView() view.backgroundColor = UIColor(hexStr: "#EDEDED") return view }() override init(style: CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) backgroundColor = .clear selectionStyle = .none setupSubviews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func awakeFromNib() { super.awakeFromNib() // Initialization code } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // Configure the view for the selected state } }