
主要变更: 1. 从 AppDelegate 中移除 Core Data 相关的属性和方法,简化应用结构。 2. 新增 EPBaseListViewController 作为消息列表的基础类,提供通用的表视图功能。 3. 添加 EPMessageListVC、EPFriendListVC、EPFollowingListVC 和 EPFansListVC,分别用于展示消息、朋友、关注和粉丝列表。 4. 引入 EPMessageSegmentView 以支持消息主界面的分段控制。 此更新旨在提升代码的可维护性,简化数据管理,并增强用户界面的功能性和交互性。
544 lines
17 KiB
Swift
544 lines
17 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 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)
|
||
}
|
||
}
|