// // EPEditSettingViewController.swift // YuMi // // Created by AI on 2025-01-27. // import UIKit import Photos import SnapKit /// 设置编辑页面 /// 支持头像更新、昵称修改和退出登录功能 class EPEditSettingViewController: UIViewController { // MARK: - UI Components private lazy var tableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) tableView.backgroundColor = UIColor(hex: "#0C0527") tableView.separatorStyle = .none tableView.delegate = self tableView.dataSource = self tableView.register(UITableViewCell.self, forCellReuseIdentifier: "SettingCell") return tableView }() private lazy var profileImageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFill imageView.layer.cornerRadius = 50 imageView.layer.masksToBounds = true imageView.backgroundColor = .systemGray5 imageView.isUserInteractionEnabled = true return imageView }() // MARK: - Data private var settingItems: [SettingItem] = [] private var userInfo: UserInfoModel? // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() navigationController?.setNavigationBarHidden(true, animated: false) setupUI() setupData() loadUserInfo() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 显示导航栏 navigationController?.setNavigationBarHidden(false, animated: animated) navigationController?.navigationBar.titleTextAttributes = [ .foregroundColor: UIColor.white, .font: UIFont.systemFont(ofSize: 18, weight: .medium) ] navigationController?.navigationBar.barTintColor = UIColor(hex: "#0C0527") navigationController?.navigationBar.tintColor = .white navigationController?.navigationBar.isTranslucent = false } // MARK: - Setup private func setupUI() { view.backgroundColor = UIColor(hex: "#0C0527") title = YMLocalizedString("EPEditSetting.Title") view.addSubview(tableView) tableView.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide.snp.top) make.leading.trailing.bottom.equalToSuperview() } // 添加头像点击手势 let tapGesture = UITapGestureRecognizer(target: self, action: #selector(profileImageTapped)) profileImageView.addGestureRecognizer(tapGesture) } private func setupData() { settingItems = [ SettingItem( title: YMLocalizedString("EPEditSetting.PersonalInfo"), action: { [weak self] in self?.handleReservedAction("PersonalInfo") } ), SettingItem( title: YMLocalizedString("EPEditSetting.Help"), action: { [weak self] in self?.handleReservedAction("Help") } ), SettingItem( title: YMLocalizedString("EPEditSetting.ClearCache"), action: { [weak self] in self?.handleReservedAction("ClearCache") } ), SettingItem( title: YMLocalizedString("EPEditSetting.CheckUpdate"), action: { [weak self] in self?.handleReservedAction("CheckUpdate") } ), SettingItem( title: YMLocalizedString("EPEditSetting.Logout"), style: .default, action: { [weak self] in self?.showLogoutConfirm() } ), SettingItem( title: YMLocalizedString("EPEditSetting.AboutUs"), action: { [weak self] in self?.handleReservedAction("AboutUs") } ) ] } private func loadUserInfo() { // 获取当前用户信息 guard let uid = AccountInfoStorage.instance().getUid(), !uid.isEmpty else { print("[EPEditSetting] 未登录,无法获取用户信息") return } // TODO: 调用API获取用户详细信息 // 这里暂时创建默认的UserInfoModel用于显示 let tempUserInfo = UserInfoModel() tempUserInfo.nick = "User" tempUserInfo.avatar = "" userInfo = tempUserInfo updateProfileImage() tableView.reloadData() } private func updateProfileImage() { guard let avatarUrl = userInfo?.avatar, !avatarUrl.isEmpty else { profileImageView.image = UIImage(systemName: "person.circle.fill") return } // 使用SDWebImage加载头像 if let url = URL(string: avatarUrl) { profileImageView.sd_setImage(with: url, placeholderImage: UIImage(systemName: "person.circle.fill")) } } // MARK: - Actions @objc private func profileImageTapped() { showAvatarSelectionSheet() } @objc private func openSettings() { // 预留设置按钮功能 handleReservedAction("Settings") } private func showAvatarSelectionSheet() { let alert = UIAlertController(title: YMLocalizedString("EPEditSetting.EditNickname"), message: nil, preferredStyle: .actionSheet) // 拍照选项 alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Camera"), style: .default) { [weak self] _ in self?.checkCameraPermissionAndPresent() }) // 相册选项 alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.PhotoLibrary"), style: .default) { [weak self] _ in self?.checkPhotoLibraryPermissionAndPresent() }) alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Cancel"), style: .cancel)) // iPad支持 if let popover = alert.popoverPresentationController { popover.sourceView = profileImageView popover.sourceRect = profileImageView.bounds } present(alert, animated: true) } private func checkCameraPermissionAndPresent() { YYUtility.checkCameraAvailable { [weak self] in self?.presentImagePicker(sourceType: .camera) } denied: { [weak self] in self?.showPermissionAlert(title: "Camera Access", message: "Please allow camera access in Settings") } restriction: { [weak self] in self?.showPermissionAlert(title: "Camera Restricted", message: "Camera access is restricted on this device") } } private func checkPhotoLibraryPermissionAndPresent() { YYUtility.checkAssetsLibrayAvailable { [weak self] in self?.presentImagePicker(sourceType: .photoLibrary) } denied: { [weak self] in self?.showPermissionAlert(title: "Photo Library Access", message: "Please allow photo library access in Settings") } restriction: { [weak self] in self?.showPermissionAlert(title: "Photo Library Restricted", message: "Photo library access is restricted on this device") } } private func presentImagePicker(sourceType: UIImagePickerController.SourceType) { let imagePicker = UIImagePickerController() imagePicker.delegate = self imagePicker.sourceType = sourceType imagePicker.allowsEditing = true present(imagePicker, animated: true) } private func showPermissionAlert(title: String, message: String) { let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Settings", style: .default) { _ in if let settingsURL = URL(string: UIApplication.openSettingsURLString) { UIApplication.shared.open(settingsURL) } }) alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Cancel"), style: .cancel)) present(alert, animated: true) } private func showNicknameEditAlert() { let alert = UIAlertController( title: YMLocalizedString("EPEditSetting.EditNickname"), message: nil, preferredStyle: .alert ) alert.addTextField { [weak self] textField in textField.text = self?.userInfo?.nick ?? "" textField.placeholder = YMLocalizedString("EPEditSetting.EnterNickname") } alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Cancel"), style: .cancel)) alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Confirm"), style: .default) { [weak self] _ in guard let newNickname = alert.textFields?.first?.text, !newNickname.isEmpty else { return } self?.updateNickname(newNickname) }) present(alert, animated: true) } private func updateNickname(_ newNickname: String) { // 构建UserInfoModel并调用更新方法 let userInfo = UserInfoModel() userInfo.nick = newNickname // 调用Presenter方法 (桥接到OC) let presenter = XPMineUserInfoEditPresenter() presenter.getUserInfoEditDataSource(withUserInfo: userInfo) // 更新本地显示 self.userInfo?.nick = newNickname tableView.reloadData() print("[EPEditSetting] 昵称更新为: \(newNickname)") } private func showLogoutConfirm() { let alert = UIAlertController( title: YMLocalizedString("EPEditSetting.LogoutConfirm"), message: nil, preferredStyle: .alert ) alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Cancel"), style: .cancel)) alert.addAction(UIAlertAction(title: YMLocalizedString("EPEditSetting.Logout"), style: .destructive) { [weak self] _ in self?.performLogout() }) present(alert, animated: true) } private func performLogout() { guard let account = AccountInfoStorage.instance().accountModel else { print("[EPEditSetting] 账号信息不存在") return } // 调用登出API Api.logoutCurrentAccount({ [weak self] (data, code, msg) in DispatchQueue.main.async { // 清除本地数据 AccountInfoStorage.instance().saveAccountInfo(nil) AccountInfoStorage.instance().saveTicket(nil) // 跳转登录页 self?.navigateToLogin() } }, access_token: account.access_token) } private func navigateToLogin() { let loginVC = EPLoginViewController() let nav = UINavigationController(rootViewController: loginVC) if let window = UIApplication.shared.windows.first { window.rootViewController = nav window.makeKeyAndVisible() } print("[EPEditSetting] 已跳转到登录页面") } private func handleReservedAction(_ title: String) { print("[\(title)] - 功能预留,待后续实现") // TODO: Phase 2 implementation // 显示占位提示 let alert = UIAlertController(title: "Coming Soon", message: "This feature will be available in the next update.", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default)) present(alert, animated: true) } } // MARK: - UITableViewDataSource & UITableViewDelegate extension EPEditSettingViewController: UITableViewDataSource, UITableViewDelegate { func numberOfSections(in tableView: UITableView) -> Int { return 2 // 头像昵称section + 设置项section } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if section == 0 { return 2 // 头像 + 昵称 } else { return settingItems.count } } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "SettingCell", for: indexPath) cell.backgroundColor = UIColor(hex: "#0C0527") cell.textLabel?.textColor = .white cell.selectionStyle = .none if indexPath.section == 0 { // 头像昵称section if indexPath.row == 0 { // 头像行 cell.textLabel?.text = YMLocalizedString("EPEditSetting.Avatar") cell.accessoryType = .disclosureIndicator // 添加头像图片 if cell.contentView.subviews.contains(profileImageView) { profileImageView.removeFromSuperview() } cell.contentView.addSubview(profileImageView) profileImageView.snp.makeConstraints { make in make.trailing.equalToSuperview().offset(-50) make.centerY.equalToSuperview() make.size.equalTo(100) } } else { // 昵称行 cell.textLabel?.text = YMLocalizedString("EPEditSetting.Nickname") cell.detailTextLabel?.text = userInfo?.nick ?? "未设置" cell.detailTextLabel?.textColor = .lightGray cell.accessoryType = .disclosureIndicator } } else { // 设置项section let item = settingItems[indexPath.row] cell.textLabel?.text = item.title cell.accessoryType = .disclosureIndicator if item.style == .default { cell.textLabel?.textColor = .systemRed } else { cell.textLabel?.textColor = .white } } return cell } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if indexPath.section == 0 && indexPath.row == 0 { return 120 // 头像行更高 } return 60 } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) if indexPath.section == 0 { if indexPath.row == 0 { // 头像点击 showAvatarSelectionSheet() } else { // 昵称点击 showNicknameEditAlert() } } else { // 设置项点击 let item = settingItems[indexPath.row] item.action() } } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return section == 0 ? 20 : 10 } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let view = UIView() view.backgroundColor = UIColor(hex: "#0C0527") return view } func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { return 10 } func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { let view = UIView() view.backgroundColor = UIColor(hex: "#0C0527") return view } } // MARK: - UIImagePickerControllerDelegate & UINavigationControllerDelegate extension EPEditSettingViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { picker.dismiss(animated: true) guard let image = info[.editedImage] as? UIImage ?? info[.originalImage] as? UIImage else { print("[EPEditSetting] 未能获取选择的图片") return } // 更新头像显示 profileImageView.image = image // 上传头像到腾讯云 uploadAvatar(image) } func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) } private func uploadAvatar(_ image: UIImage) { // 压缩图片 guard let imageData = image.jpegData(compressionQuality: 0.5) else { print("[EPEditSetting] 图片压缩失败") return } // 生成文件名 let format = "jpg" let name = "image/\(UUID().uuidString).\(format)" // 上传到腾讯云 UploadFile.share().qCloudUploadImage(imageData, named: name, success: { [weak self] (key, resp) in print("[EPEditSetting] 头像上传成功: \(key)") // 调用API更新头像 self?.updateAvatarAPI(avatarUrl: key) }, failure: { (resCode, message) in print("[EPEditSetting] 头像上传失败: \(message)") }) } private func updateAvatarAPI(avatarUrl: String) { // 调用API更新头像 Api.userV2UploadAvatar({ [weak self] (data, code, msg) in DispatchQueue.main.async { if code == 200 { print("[EPEditSetting] 头像更新成功") // 更新本地用户信息 self?.userInfo?.avatar = avatarUrl } else { print("[EPEditSetting] 头像更新失败: \(String(describing: msg))") } } }, avatarUrl: avatarUrl, needPay: NSNumber(value: false)) } } // MARK: - Helper Models private struct SettingItem { let title: String let style: UITableViewCell.SelectionStyle let action: () -> Void init(title: String, style: UITableViewCell.SelectionStyle = .default, action: @escaping () -> Void) { self.title = title self.style = style self.action = action } } // MARK: - UIColor Extension private extension UIColor { convenience init(hex: String) { let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) var int: UInt64 = 0 Scanner(string: hex).scanHexInt64(&int) let a, r, g, b: UInt64 switch hex.count { case 3: // RGB (12-bit) (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) case 6: // RGB (24-bit) (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) case 8: // ARGB (32-bit) (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) default: (a, r, g, b) = (1, 1, 1, 0) } self.init( red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255 ) } }