Files
real-e-party-iOS/YuMi/E-P/TabBar/EPTabBarController.swift
2025-10-17 14:52:29 +08:00

520 lines
16 KiB
Swift

// 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 mineButton = createTabButton(
normalImage: "tab_mine_off",
selectedImage: "tab_mine_on",
tag: 1
)
tabButtons = [momentButton, 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.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() {
// TODO: 使
let blankVC1 = UIViewController()
blankVC1.view.backgroundColor = .white
blankVC1.tabBarItem = createTabBarItem(
title: YMLocalizedString("tab.moment"),
normalImage: "tab_moment_normal",
selectedImage: "tab_moment_selected"
)
let blankVC2 = UIViewController()
blankVC2.view.backgroundColor = .white
blankVC2.tabBarItem = createTabBarItem(
title: YMLocalizedString("tab.mine"),
normalImage: "tab_mine_normal",
selectedImage: "tab_mine_selected"
)
viewControllers = [blankVC1, blankVC2]
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() {
if viewControllers?.count != 2 ||
!(viewControllers?[0] is UINavigationController) ||
!(viewControllers?[1] is UINavigationController) {
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"
)
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, mineNav]
NSLog("[EPTabBarController] 登录后 ViewControllers 创建完成 - Moment & 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)
}
}