| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- //
- // 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") ?? "登录"
- 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 isLoggedIn = UserDefaults.standard.bool(forKey: "isLogggedIn")
- let userToken = UserDefaults.standard.string(forKey: "userToken")
-
- // 如果没有登录状态或者没有有效的token,弹出登录界面
- if !isLoggedIn || userToken?.isEmpty != false {
- showLoginViewController()
- return
- }
-
- // 已登录,跳转到个人信息页
- let userSettingsVC = UserSettingsViewController()
- userSettingsVC.hidesBottomBarWhenPushed = true
- navigationController?.pushViewController(userSettingsVC, animated: true)
- }
-
- private func showLoginViewController() {
- let loginVC = LoginViewController()
- let navController = UINavigationController(rootViewController: loginVC)
- navController.modalPresentationStyle = .fullScreen
- present(navController, animated: true)
- }
- @objc private func tapRate() {
- // 跳 App Store 评分(占位)
- SKStoreReviewController.requestReview()
- }
-
- @objc private func tapPet() {
-
- // 检查登录状态
- let isLoggedIn = UserDefaults.standard.bool(forKey: "isLogggedIn")
- let userToken = UserDefaults.standard.string(forKey: "userToken")
-
- // 如果没有登录状态或者没有有效的token,弹出登录界面
- if !isLoggedIn || userToken?.isEmpty != false {
- showLoginViewController()
- return
- }
- //进入我的宠物界面
- 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)
- }
- }
|