Files
real-e-party-iOS/YuMi/E-P/NewTabBar/EPTabBarController.swift
edwinQQQ c8173bf034 refactor: 移除 Core Data 相关代码并添加新的消息列表视图控制器
主要变更:
1. 从 AppDelegate 中移除 Core Data 相关的属性和方法,简化应用结构。
2. 新增 EPBaseListViewController 作为消息列表的基础类,提供通用的表视图功能。
3. 添加 EPMessageListVC、EPFriendListVC、EPFollowingListVC 和 EPFansListVC,分别用于展示消息、朋友、关注和粉丝列表。
4. 引入 EPMessageSegmentView 以支持消息主界面的分段控制。

此更新旨在提升代码的可维护性,简化数据管理,并增强用户界面的功能性和交互性。
2025-10-20 11:25:33 +08:00

544 lines
17 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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)
}
}