// // SettingsPopupView.swift // RoderickRalph // // Created by Neoa on 2025/8/19. // import Foundation import UIKit // 在文件顶部 import 之后、class 之前加: protocol OverlayPanelAgent: AnyObject { func overlayPanelDidDismiss(_ panel: OverlayPanelView) } class OverlayPanelView: UIView { weak var delegate: OverlayPanelAgent? // MARK: - UI Components private let backdropView: UIView = { let view = UIView() view.backgroundColor = UIColor.black.withAlphaComponent(0.5) // Semi-transparent gray background view.translatesAutoresizingMaskIntoConstraints = false return view }() private let titleButton: UIButton = { let button = UIButton(type: .system) button.setBackgroundImage(UIImage(named: "res_x8brwne3"), for: .normal) button.setTitle("设置", for: .normal) button.setTitleColor(.white, for: .normal) button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 18) button.translatesAutoresizingMaskIntoConstraints = false return button }() private let avatarFrameView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "res_b8xhu2tz") iv.contentMode = .scaleAspectFit iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let avatarImageView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "catlogos") iv.contentMode = .scaleAspectFit iv.layer.cornerRadius = 24 iv.clipsToBounds = true iv.translatesAutoresizingMaskIntoConstraints = false return iv }() private let nicknameLabel: UILabel = { let label = UILabel() label.text = "昵 称" label.font = UIFont.systemFont(ofSize: 12) label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false return label }() private let nicknameValueLabel: UILabel = { let label = UILabel() label.text = "XXXX" label.backgroundColor = .white label.font = UIFont.systemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") label.layer.cornerRadius = 4 label.clipsToBounds = true label.textAlignment = .center label.translatesAutoresizingMaskIntoConstraints = false return label }() private let roleIdLabel: UILabel = { let label = UILabel() label.text = "角色ID" label.font = UIFont.systemFont(ofSize: 12) label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false return label }() private let roleIdValueLabel: UILabel = { let label = UILabel() label.text = "XXXX" label.backgroundColor = .white label.font = UIFont.systemFont(ofSize: 12) label.textColor = UIColor(hexString:"#E28814") label.layer.cornerRadius = 4 label.clipsToBounds = true label.textAlignment = .center label.translatesAutoresizingMaskIntoConstraints = false return label }() private let dismissButton: UIButton = { let button = UIButton(type: .custom) button.setImage(UIImage(named: "res_9mx2yiyi"), for: .normal) button.addTarget(self, action: #selector(dismissPanel), for: .touchUpInside) button.translatesAutoresizingMaskIntoConstraints = false return button }() private let musicLabel: UILabel = { let label = UILabel() label.text = "音乐" label.font = UIFont.systemFont(ofSize: 12) label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false return label }() private let musicToggle: UISwitch = { let switchControl = UISwitch() switchControl.isOn = false switchControl.translatesAutoresizingMaskIntoConstraints = false return switchControl }() private let sfxLabel: UILabel = { let label = UILabel() label.text = "音效" label.font = UIFont.systemFont(ofSize: 12) label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false return label }() private let sfxToggle: UISwitch = { let switchControl = UISwitch() switchControl.isOn = false switchControl.translatesAutoresizingMaskIntoConstraints = false return switchControl }() private let registeredAtLabel: UILabel = { let label = UILabel() label.text = "注册时间: 2025-05-26 17:58:48" label.font = UIFont.systemFont(ofSize: 10) label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false return label }() private let appNameLabel: UILabel = { let label = UILabel() label.text = "青柠檬记账" label.font = UIFont.systemFont(ofSize: 10) label.textColor = .white label.translatesAutoresizingMaskIntoConstraints = false return label }() private let panelImageView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(named: "res_ew6zkg25") // Background image for the pop-up imageView.contentMode = .scaleToFill imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() // MARK: - Initialization init() { super.init(frame: .zero) buildInterface() applyConstraints() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - UI Setup private func buildInterface() { addSubview(backdropView) backdropView.isUserInteractionEnabled = true // 允许事件穿透 addSubview(panelImageView) addSubview(titleButton) addSubview(avatarFrameView) addSubview(avatarImageView) addSubview(nicknameLabel) addSubview(nicknameValueLabel) addSubview(roleIdLabel) addSubview(roleIdValueLabel) addSubview(musicLabel) addSubview(sfxLabel) addSubview(musicToggle) addSubview(sfxToggle) addSubview(dismissButton) addSubview(registeredAtLabel) addSubview(appNameLabel) // Prefill from saved user info (if available) let nickname = UserDefaults.standard.string(forKey: "nickname") ?? "XXXX" let roleID = UserDefaults.standard.string(forKey: "roleID") ?? "XXXX" let regTime = UserDefaults.standard.string(forKey: "registryTimeStr") ?? "--" nicknameValueLabel.text = nickname roleIdValueLabel.text = roleID registeredAtLabel.text = "注册时间: \(regTime)" // 显示已选择的渠道名(如果有),否则使用默认占位 if let savedChannel = UserDefaults.standard.string(forKey: "selectedChannelName"), !savedChannel.isEmpty { appNameLabel.text = savedChannel } let head = UserDefaults.standard.string(forKey: "headImgURL") assignAvatar(with: head) // 最后添加背景视图(确保它在最底层) // 添加背景点击手势 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(backdropTapped)) backdropView.addGestureRecognizer(tapGesture) } private func applyConstraints() { NSLayoutConstraint.activate([ // Background (Gray) for the outer area backdropView.topAnchor.constraint(equalTo: topAnchor), backdropView.bottomAnchor.constraint(equalTo: bottomAnchor), backdropView.leadingAnchor.constraint(equalTo: leadingAnchor), backdropView.trailingAnchor.constraint(equalTo: trailingAnchor), // Popup background image panelImageView.centerXAnchor.constraint(equalTo: centerXAnchor), panelImageView.centerYAnchor.constraint(equalTo: centerYAnchor), panelImageView.widthAnchor.constraint(equalToConstant: 333), panelImageView.heightAnchor.constraint(equalToConstant: 215), // Close Button dismissButton.topAnchor.constraint(equalTo: panelImageView.topAnchor, constant: -12), dismissButton.trailingAnchor.constraint(equalTo: panelImageView.trailingAnchor, constant: 12), dismissButton.widthAnchor.constraint(equalToConstant: 24), dismissButton.heightAnchor.constraint(equalToConstant: 24), titleButton.topAnchor.constraint(equalTo: panelImageView.topAnchor, constant: 10), titleButton.centerXAnchor.constraint(equalTo: panelImageView.centerXAnchor, constant: 0), titleButton.widthAnchor.constraint(equalToConstant: 132), titleButton.heightAnchor.constraint(equalToConstant: 37), avatarFrameView.topAnchor.constraint(equalTo: titleButton.bottomAnchor, constant: 5), avatarFrameView.leadingAnchor.constraint(equalTo: panelImageView.leadingAnchor, constant: 33), avatarFrameView.widthAnchor.constraint(equalToConstant: 58), avatarFrameView.heightAnchor.constraint(equalToConstant: 58), avatarImageView.centerXAnchor.constraint(equalTo: avatarFrameView.centerXAnchor), avatarImageView.centerYAnchor.constraint(equalTo: avatarFrameView.centerYAnchor), avatarImageView.widthAnchor.constraint(equalToConstant: 48), avatarImageView.heightAnchor.constraint(equalToConstant: 48), nicknameValueLabel.trailingAnchor.constraint(equalTo: panelImageView.trailingAnchor,constant: -35), nicknameValueLabel.topAnchor.constraint(equalTo: titleButton.bottomAnchor,constant: 14), nicknameValueLabel.widthAnchor.constraint(equalToConstant: 130), nicknameValueLabel.heightAnchor.constraint(equalToConstant: 24), nicknameLabel.trailingAnchor.constraint(equalTo: nicknameValueLabel.leadingAnchor,constant: -12), nicknameLabel.centerYAnchor.constraint(equalTo: nicknameValueLabel.centerYAnchor,constant: 0), roleIdValueLabel.trailingAnchor.constraint(equalTo: nicknameValueLabel.trailingAnchor,constant: 0), roleIdValueLabel.topAnchor.constraint(equalTo: nicknameValueLabel.bottomAnchor,constant: 3), roleIdValueLabel.widthAnchor.constraint(equalToConstant: 130), roleIdValueLabel.heightAnchor.constraint(equalToConstant: 24), roleIdLabel.trailingAnchor.constraint(equalTo: roleIdValueLabel.leadingAnchor,constant: -12), roleIdLabel.centerYAnchor.constraint(equalTo: roleIdValueLabel.centerYAnchor,constant: 0), musicLabel.leadingAnchor.constraint(equalTo: avatarFrameView.leadingAnchor,constant: 0), musicLabel.topAnchor.constraint(equalTo: avatarFrameView.bottomAnchor,constant: 24), // Music Switch musicToggle.centerYAnchor.constraint(equalTo: musicLabel.centerYAnchor, constant: 0), musicToggle.leadingAnchor.constraint(equalTo: musicLabel.trailingAnchor, constant: 10), // Sound Switch sfxToggle.centerYAnchor.constraint(equalTo: musicToggle.centerYAnchor), sfxToggle.trailingAnchor.constraint(equalTo: panelImageView.trailingAnchor, constant: -28), sfxLabel.centerYAnchor.constraint(equalTo: musicLabel.centerYAnchor,constant: 0), sfxLabel.trailingAnchor.constraint(equalTo: sfxToggle.trailingAnchor,constant: -60), // Registration Time Label registeredAtLabel.leadingAnchor.constraint(equalTo: musicLabel.leadingAnchor, constant: 0), registeredAtLabel.topAnchor.constraint(equalTo: musicLabel.bottomAnchor,constant: 22), appNameLabel.centerYAnchor.constraint(equalTo: registeredAtLabel.centerYAnchor, constant: 0), appNameLabel.trailingAnchor.constraint(equalTo: sfxToggle.trailingAnchor,constant: 0), ]) } // MARK: - Actions @objc private func dismissPanel() { delegate?.overlayPanelDidDismiss(self) removeFromSuperview() } @objc private func backdropTapped() { dismissPanel() } // MARK: - Avatar Loading private static let avatarCache = NSCache() private func assignAvatar(with urlString: String?) { guard let s = urlString, !s.isEmpty, let url = URL(string: s) else { avatarImageView.image = UIImage(named: "catlogos") return } if let cached = OverlayPanelView.avatarCache.object(forKey: url as NSURL) { avatarImageView.image = cached return } URLSession.shared.dataTask(with: url) { data, _, _ in guard let data = data, let img = UIImage(data: data) else { return } OverlayPanelView.avatarCache.setObject(img, forKey: url as NSURL) DispatchQueue.main.async { [weak self] in self?.avatarImageView.image = img } }.resume() } }