// Created by AI on 2025-10-09. // Copyright © 2025 YuMi. All rights reserved. import UIKit import SnapKit @objc class EPTabBarController: UITabBarController { // MARK: - Properties private var isLoggedIn: Bool = false private var customTabBarView: UIView! private var tabBarBackgroundView: UIVisualEffectView! private var tabButtons: [UIButton] = [] // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() #if DEBUG APIConfig.testEncryption() #endif self.tabBar.isHidden = true self.delegate = self performAutoLogin() setupCustomFloatingTabBar() setupInitialViewControllers() NSLog("[EPTabBarController] 悬浮 TabBar 初始化完成") } deinit { NSLog("[EPTabBarController] 已释放") } // MARK: - Setup private func setupCustomFloatingTabBar() { customTabBarView = UIView() customTabBarView.translatesAutoresizingMaskIntoConstraints = false customTabBarView.backgroundColor = .clear view.addSubview(customTabBarView) let effect: UIVisualEffect if #available(iOS 26.0, *) { effect = UIGlassEffect() } else { effect = UIBlurEffect(style: .systemMaterial) } tabBarBackgroundView = UIVisualEffectView(effect: effect) tabBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false tabBarBackgroundView.layer.cornerRadius = 28 tabBarBackgroundView.layer.masksToBounds = true tabBarBackgroundView.layer.borderWidth = 0.5 tabBarBackgroundView.layer.borderColor = UIColor.white.withAlphaComponent(0.2).cgColor customTabBarView.addSubview(tabBarBackgroundView) customTabBarView.snp.makeConstraints { make in make.leading.equalTo(view).offset(16) make.trailing.equalTo(view).offset(-16) make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-12) make.height.equalTo(64) } tabBarBackgroundView.snp.makeConstraints { make in make.edges.equalTo(customTabBarView) } setupTabButtons() NSLog("[EPTabBarController] 悬浮 TabBar 设置完成") } private func setupTabButtons() { let momentButton = createTabButton( normalImage: "tab_moment_off", selectedImage: "tab_moment_on", tag: 0 ) let messageButton = createTabButton( normalImage: "tab_message_off", selectedImage: "tab_message_on", tag: 1 ) let mineButton = createTabButton( normalImage: "tab_mine_off", selectedImage: "tab_mine_on", tag: 2 ) tabButtons = [momentButton, messageButton, mineButton] let stackView = UIStackView(arrangedSubviews: tabButtons) stackView.axis = .horizontal stackView.distribution = .fillEqually stackView.spacing = 20 stackView.translatesAutoresizingMaskIntoConstraints = false tabBarBackgroundView.contentView.addSubview(stackView) stackView.snp.makeConstraints { make in make.top.equalTo(tabBarBackgroundView).offset(8) make.leading.equalTo(tabBarBackgroundView).offset(20) make.trailing.equalTo(tabBarBackgroundView).offset(-20) make.bottom.equalTo(tabBarBackgroundView).offset(-8) } updateTabButtonStates(selectedIndex: 0) } private func createTabButton(normalImage: String, selectedImage: String, tag: Int) -> UIButton { let button = UIButton(type: .custom) button.tag = tag button.adjustsImageWhenHighlighted = false if let normalImg = UIImage(named: normalImage), let selectedImg = UIImage(named: selectedImage) { button.setImage(normalImg, for: .normal) button.setImage(selectedImg, for: .selected) } else { let fallbackIcons = ["sparkles", "person.circle"] let iconName = fallbackIcons[tag] let imageConfig = UIImage.SymbolConfiguration(pointSize: 24, weight: .medium) let normalIcon = UIImage(systemName: iconName, withConfiguration: imageConfig) button.setImage(normalIcon, for: .normal) button.setImage(normalIcon, for: .selected) button.tintColor = .white.withAlphaComponent(0.6) } button.imageView?.contentMode = .scaleAspectFit button.setTitle(nil, for: .normal) button.setTitle(nil, for: .selected) button.imageView?.snp.makeConstraints { make in make.size.equalTo(28) } button.addTarget(self, action: #selector(tabButtonTapped(_:)), for: .touchUpInside) return button } @objc private func tabButtonTapped(_ sender: UIButton) { let newIndex = sender.tag if newIndex == selectedIndex { return } updateTabButtonStates(selectedIndex: newIndex) UIView.performWithoutAnimation { selectedIndex = newIndex } let tabNames = [YMLocalizedString("tab.moment"), YMLocalizedString("tab.message"), YMLocalizedString("tab.mine")] NSLog("[EPTabBarController] 选中 Tab: \(tabNames[newIndex])") } private func updateTabButtonStates(selectedIndex: Int) { tabButtons.forEach { $0.isUserInteractionEnabled = false } for (index, button) in tabButtons.enumerated() { let isSelected = (index == selectedIndex) button.isSelected = isSelected if button.currentImage?.isSymbolImage == true { button.tintColor = isSelected ? .white : .white.withAlphaComponent(0.6) } UIView.animate(withDuration: 0.2, delay: 0, options: [.curveEaseOut], animations: { button.transform = isSelected ? CGAffineTransform(scaleX: 1.1, y: 1.1) : .identity }) } DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { self.tabButtons.forEach { $0.isUserInteractionEnabled = true } } } private func setupInitialViewControllers() { // 三栏占位(Moment | Message | Mine) let momentVC = UIViewController() momentVC.view.backgroundColor = .systemBlue momentVC.title = "Moment" let momentNav = UINavigationController(rootViewController: momentVC) momentNav.tabBarItem = createTabBarItem(title: YMLocalizedString("tab.moment"), normalImage: "tab_moment_normal", selectedImage: "tab_moment_selected") let messageVC = EPMessageMainViewController() messageVC.title = "Message" let messageNav = UINavigationController(rootViewController: messageVC) messageNav.tabBarItem = createTabBarItem(title: YMLocalizedString("tab.message"), normalImage: "tab_message_normal", selectedImage: "tab_message_selected") // 角标绑定 messageVC.unreadCountDidChange = { [weak self] c in let value: String? = c > 0 ? (c > 99 ? "99+" : "\(c)") : nil self?.viewControllers?[1].tabBarItem.badgeValue = value } let mineVC = UIViewController() mineVC.view.backgroundColor = .systemGreen mineVC.title = "Mine" let mineNav = UINavigationController(rootViewController: mineVC) mineNav.tabBarItem = createTabBarItem(title: YMLocalizedString("tab.mine"), normalImage: "tab_mine_normal", selectedImage: "tab_mine_selected") viewControllers = [momentNav, messageNav, mineNav] selectedIndex = 0 NSLog("[EPTabBarController] 初始 ViewControllers 设置完成") } private func createTabBarItem(title: String, normalImage: String, selectedImage: String) -> UITabBarItem { let item = UITabBarItem( title: title, image: UIImage(named: normalImage)?.withRenderingMode(.alwaysOriginal), selectedImage: UIImage(named: selectedImage)?.withRenderingMode(.alwaysOriginal) ) return item } // MARK: - Public Methods func refreshTabBar(isLogin: Bool) { isLoggedIn = isLogin if isLogin { setupLoggedInViewControllers() } else { setupInitialViewControllers() } NSLog("[EPTabBarController] TabBar 已刷新,登录状态: \(isLogin)") } private func setupLoggedInViewControllers() { // 创建动态页 let momentVC = EPMomentViewController() momentVC.title = YMLocalizedString("tab.moment") let momentNav = createTransparentNavigationController( rootViewController: momentVC, tabTitle: YMLocalizedString("tab.moment"), normalImage: "tab_moment_normal", selectedImage: "tab_moment_selected" ) // 创建消息页(Swift UIKit 容器) let messageVC = EPMessageMainViewController() let messageNav = createTransparentNavigationController( rootViewController: messageVC, tabTitle: YMLocalizedString("tab.message"), normalImage: "tab_message_normal", selectedImage: "tab_message_selected" ) // 角标绑定 messageVC.unreadCountDidChange = { [weak self] c in let value: String? = c > 0 ? (c > 99 ? "99+" : "\(c)") : nil self?.viewControllers?[1].tabBarItem.badgeValue = value } // 创建我的页 let mineVC = EPMineViewController() mineVC.title = YMLocalizedString("tab.mine") let mineNav = createTransparentNavigationController( rootViewController: mineVC, tabTitle: YMLocalizedString("tab.mine"), normalImage: "tab_mine_normal", selectedImage: "tab_mine_selected" ) viewControllers = [momentNav, messageNav, mineNav] NSLog("[EPTabBarController] 登录后 ViewControllers 创建完成 - Moment & Message & Mine") selectedIndex = 0 } private func createTransparentNavigationController( rootViewController: UIViewController, tabTitle: String, normalImage: String, selectedImage: String ) -> UINavigationController { let nav = UINavigationController(rootViewController: rootViewController) nav.navigationBar.isTranslucent = true nav.navigationBar.setBackgroundImage(UIImage(), for: .default) nav.navigationBar.shadowImage = UIImage() nav.view.backgroundColor = .clear nav.tabBarItem = createTabBarItem( title: tabTitle, normalImage: normalImage, selectedImage: selectedImage ) nav.delegate = self return nav } // MARK: - TabBar Visibility Control private func showCustomTabBar(animated: Bool = true) { guard customTabBarView.isHidden else { return } if animated { customTabBarView.isHidden = false customTabBarView.alpha = 0 customTabBarView.transform = CGAffineTransform(translationX: 0, y: 20) UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) { self.customTabBarView.alpha = 1 self.customTabBarView.transform = .identity } } else { customTabBarView.isHidden = false customTabBarView.alpha = 1 } } private func hideCustomTabBar(animated: Bool = true) { guard !customTabBarView.isHidden else { return } if animated { UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn, animations: { self.customTabBarView.alpha = 0 self.customTabBarView.transform = CGAffineTransform(translationX: 0, y: 20) }) { _ in self.customTabBarView.isHidden = true self.customTabBarView.transform = .identity } } else { customTabBarView.isHidden = true customTabBarView.alpha = 0 } } } // MARK: - UITabBarControllerDelegate extension EPTabBarController: UITabBarControllerDelegate { override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) { NSLog("[EPTabBarController] 选中 Tab: \(item.title ?? "Unknown")") } func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { return nil } func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { return true } } // MARK: - UINavigationControllerDelegate extension EPTabBarController: UINavigationControllerDelegate { func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { let isRootViewController = navigationController.viewControllers.count == 1 if isRootViewController { showCustomTabBar(animated: animated) NSLog("[EPTabBarController] 显示 TabBar - 根页面") } else { hideCustomTabBar(animated: animated) NSLog("[EPTabBarController] 隐藏 TabBar - 子页面 (层级: \(navigationController.viewControllers.count))") } } } // MARK: - Auto Login & Ticket Validation extension EPTabBarController { private func performAutoLogin() { guard let accountModel = AccountInfoStorage.instance().getCurrentAccountInfo() else { NSLog("[EPTabBarController] ⚠️ 账号信息不存在,跳转到登录页") handleTokenInvalid() return } let uid = accountModel.uid let accessToken = accountModel.access_token guard !uid.isEmpty, !accessToken.isEmpty else { NSLog("[EPTabBarController] ⚠️ uid 或 access_token 为空,跳转到登录页") handleTokenInvalid() return } let existingTicket = AccountInfoStorage.instance().getTicket() ?? "" if !existingTicket.isEmpty { NSLog("[EPTabBarController] ✅ Ticket 已存在,自动登录成功") return } NSLog("[EPTabBarController] 🔄 Ticket 不存在,正在请求...") let loginService = EPLoginService() loginService.requestTicket(accessToken: accessToken) { ticket in NSLog("[EPTabBarController] ✅ Ticket 请求成功: \(ticket)") AccountInfoStorage.instance().saveTicket(ticket) } failure: { [weak self] code, msg in NSLog("[EPTabBarController] ❌ Ticket 请求失败 (\(code)): \(msg)") DispatchQueue.main.async { self?.handleTokenInvalid() } } } private func handleTokenInvalid() { NSLog("[EPTabBarController] ⚠️ Token 失效,清空账号数据...") AccountInfoStorage.instance().saveAccountInfo(nil) AccountInfoStorage.instance().saveTicket("") DispatchQueue.main.async { let loginVC = EPLoginViewController() let nav = BaseNavigationController(rootViewController: loginVC) nav.modalPresentationStyle = .fullScreen if #available(iOS 13.0, *) { for scene in UIApplication.shared.connectedScenes { if let windowScene = scene as? UIWindowScene, windowScene.activationState == .foregroundActive, let window = windowScene.windows.first(where: { $0.isKeyWindow }) { window.rootViewController = nav window.makeKeyAndVisible() break } } } else { if let window = UIApplication.shared.keyWindow { window.rootViewController = nav window.makeKeyAndVisible() } } NSLog("[EPTabBarController] ✅ 已跳转到登录页") } } } // MARK: - OC Compatibility extension EPTabBarController { @objc static func create() -> EPTabBarController { return EPTabBarController() } @objc func refreshTabBarWithIsLogin(_ isLogin: Bool) { refreshTabBar(isLogin: isLogin) } }