// // SettingsViewController.swift // VenusKitto // // Created by Neoa on 2025/8/27. // import Foundation import UIKit import StoreKit // MARK: - Settings final class SettingsViewController: UIViewController { // 背景图 private let bgImageView: UIImageView = { let iv = UIImageView(image: UIImage(named: "wode465")) iv.contentMode = .scaleAspectFill iv.clipsToBounds = true return iv }() private let titleLabel: UILabel = { let l = UILabel() l.text = "设置" l.font = .systemFont(ofSize: 28, weight: .semibold) l.textColor = UIColor(hex: "#2B2B2B") return l }() // 顶部用户卡片(手绘边框) private let profileCard = UIImageView(image: UIImage(named: "wode402")) private let avatarView = UIImageView() private let phoneLabel = UILabel() private let idLabel = UILabel() private let profileChevron = UIImageView(image: UIImage(systemName: "chevron.right")) // 三个功能行 private lazy var rateRow = makeRow(title: "给个好评", action: #selector(tapRate)) private lazy var peteRow = makeRow(title: "我的宠物", action: #selector(tapPet)) private lazy var feedbackRow = makeRow(title: "意见反馈", action: #selector(tapFeedback)) private lazy var aboutRow = makeRow(title: "关于我们", action: #selector(tapAbout)) override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor(hex: "#FFFEFC") setupBackground() if let backImage = UIImage(named: "AddPet385") { let backButton = UIBarButtonItem(image: backImage.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(tapCancel)) navigationItem.leftBarButtonItem = backButton } NotificationCenter.default.addObserver(self, selector: #selector(handleAvatarUpdated(_:)), name: Notification.Name("UserAvatarUpdated"), object: nil) buildUI() fillUserInfo() } deinit { NotificationCenter.default.removeObserver(self, name: Notification.Name("UserAvatarUpdated"), object: nil) } @objc private func handleAvatarUpdated(_ note: Notification) { if let urlStr = note.object as? String, let url = URL(string: urlStr) { // 直接用通知携带的新 URL 刷新头像 loadImage(from: url) { [weak self] img in self?.avatarView.image = img ?? UIImage(named: "Home372") } // 同步到本地,保证下次进入可以读到 UserDefaults.standard.set(urlStr, forKey: "memberIcon") } else if let urlStr = (UserDefaults.standard.string(forKey: "memberIcon")), let url = URL(string: urlStr) { // 兜底:没有携带 object 时,从本地读取刷新 loadImage(from: url) { [weak self] img in self?.avatarView.image = img ?? UIImage(named: "Home372") } } } private func buildUI() { // 顶部标题 view.addSubview(titleLabel) titleLabel.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 12), titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24) ]) // 用户卡片 profileCard.contentMode = .scaleToFill profileCard.isUserInteractionEnabled = true let tapCard = UITapGestureRecognizer(target: self, action: #selector(tapProfile)) profileCard.addGestureRecognizer(tapCard) view.addSubview(profileCard) profileCard.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ profileCard.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 12), profileCard.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), profileCard.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), profileCard.heightAnchor.constraint(equalToConstant: 92) ]) avatarView.backgroundColor = UIColor(hex: "#FFF4CC") avatarView.layer.cornerRadius = 12 avatarView.layer.masksToBounds = true avatarView.contentMode = .scaleAspectFill phoneLabel.font = .systemFont(ofSize: 16, weight: .semibold) phoneLabel.textColor = UIColor(hex: "#2B2B2B") idLabel.font = .systemFont(ofSize: 12) idLabel.textColor = UIColor(hex: "#9B9B9B") profileChevron.tintColor = UIColor(hex: "#5B4227") profileChevron.setContentCompressionResistancePriority(.required, for: .horizontal) [avatarView, phoneLabel, idLabel, profileChevron].forEach { $0.translatesAutoresizingMaskIntoConstraints = false profileCard.addSubview($0) } NSLayoutConstraint.activate([ avatarView.leadingAnchor.constraint(equalTo: profileCard.leadingAnchor, constant: 16), avatarView.centerYAnchor.constraint(equalTo: profileCard.centerYAnchor), avatarView.widthAnchor.constraint(equalToConstant: 56), avatarView.heightAnchor.constraint(equalToConstant: 56), profileChevron.trailingAnchor.constraint(equalTo: profileCard.trailingAnchor, constant: -12), profileChevron.centerYAnchor.constraint(equalTo: profileCard.centerYAnchor), profileChevron.widthAnchor.constraint(equalToConstant: 16), profileChevron.heightAnchor.constraint(equalToConstant: 16), phoneLabel.leadingAnchor.constraint(equalTo: avatarView.trailingAnchor, constant: 12), phoneLabel.trailingAnchor.constraint(lessThanOrEqualTo: profileChevron.leadingAnchor, constant: -8), phoneLabel.bottomAnchor.constraint(equalTo: profileCard.centerYAnchor, constant: -2), idLabel.leadingAnchor.constraint(equalTo: phoneLabel.leadingAnchor), idLabel.topAnchor.constraint(equalTo: profileCard.centerYAnchor, constant: 2), idLabel.trailingAnchor.constraint(lessThanOrEqualTo: profileChevron.leadingAnchor, constant: -8) ]) // 三个功能 row let stack = UIStackView(arrangedSubviews: [rateRow, peteRow, feedbackRow, aboutRow]) stack.axis = .vertical stack.spacing = 12 stack.alignment = .fill view.addSubview(stack) stack.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stack.topAnchor.constraint(equalTo: profileCard.bottomAnchor, constant: 16), stack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), stack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16) ]) } private func setupBackground() { view.insertSubview(bgImageView, at: 0) bgImageView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ bgImageView.topAnchor.constraint(equalTo: view.topAnchor), bgImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor), bgImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor), bgImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) } @objc private func tapCancel() { navigationController?.popViewController(animated: true) } private func makeRow(title: String, action: Selector) -> UIControl { let container = UIControl() container.backgroundColor = .white container.layer.cornerRadius = 14 container.layer.shadowColor = UIColor.black.cgColor container.layer.shadowOpacity = 0.08 container.layer.shadowRadius = 10 container.layer.shadowOffset = CGSize(width: 0, height: 4) container.heightAnchor.constraint(equalToConstant: 64).isActive = true let label = UILabel() label.text = title label.font = .systemFont(ofSize: 16) label.textColor = UIColor(hex: "#2B2B2B") let chevron = UIImageView(image: UIImage(systemName: "chevron.right")) chevron.tintColor = UIColor(hex: "#5B4227") chevron.setContentCompressionResistancePriority(.required, for: .horizontal) container.addSubview(label) container.addSubview(chevron) label.translatesAutoresizingMaskIntoConstraints = false chevron.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ label.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16), label.centerYAnchor.constraint(equalTo: container.centerYAnchor), chevron.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -16), chevron.centerYAnchor.constraint(equalTo: container.centerYAnchor), chevron.widthAnchor.constraint(equalToConstant: 16), chevron.heightAnchor.constraint(equalToConstant: 16) ]) container.addTarget(self, action: action, for: .touchUpInside) return container } private func fillUserInfo() { // 显示手机号(打码)与 ID let phone = UserDefaults.standard.string(forKey: "memberPhone") ?? "18000000000" let uid = UserDefaults.standard.string(forKey: "userId") ?? "--" phoneLabel.text = maskedPhone(phone) idLabel.text = "ID:\(uid)" if let urlStr = UserDefaults.standard.string(forKey: "memberIcon"), let url = URL(string: urlStr) { loadImage(from: url) { [weak self] img in self?.avatarView.image = img ?? UIImage(named: "Home372") } } else { avatarView.image = UIImage(named: "Home372") } } private func maskedPhone(_ s: String) -> String { let digits = s.filter { $0.isNumber } guard digits.count >= 7 else { return s } let start = digits.prefix(3) let end = digits.suffix(4) return "\(start)****\(end)" } private func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) { URLSession.shared.dataTask(with: url) { data, _, _ in guard let data = data, let img = UIImage(data: data) else { DispatchQueue.main.async { completion(nil) } return } DispatchQueue.main.async { completion(img) } }.resume() } // MARK: - Actions @objc private func tapProfile() { // 这里可跳个人信息页;先展示提示 // let ac = UIAlertController(title: "个人信息", message: "点击了头像卡片", preferredStyle: .alert) // ac.addAction(UIAlertAction(title: "好的", style: .default)) // present(ac, animated: true) let userSettingsVC = UserSettingsViewController() userSettingsVC.hidesBottomBarWhenPushed = true navigationController?.pushViewController(userSettingsVC, animated: true) } @objc private func tapRate() { // 跳 App Store 评分(占位) SKStoreReviewController.requestReview() } @objc private func tapPet() { //进入我的宠物界面 let vc = MyPetsViewController() vc.hidesBottomBarWhenPushed = true navigationController?.pushViewController(vc, animated: true) } @objc private func tapFeedback() { let vc = FeedbackViewController() vc.hidesBottomBarWhenPushed = true navigationController?.pushViewController(vc, animated: true) } @objc private func tapAbout() { let vc = AboutViewController() vc.hidesBottomBarWhenPushed = true navigationController?.pushViewController(vc, animated: true) } }