Files
real-e-party-iOS/YuMi/Modules/NewTabBar/EPTabBarController.swift
edwinQQQ 8b177e5fad fix: 消除 TabBar 切换时的页面闪烁问题
核心修复:
1. 移除导航栏动画冲突
   - 移除 viewWillAppear 中的 navigationBar 隐藏逻辑
   - ViewController 未包装在 NavigationController 中,调用导航栏方法会触发冗余动画

2. 禁用 UITabBarController 默认切换动画
   - 设置 UITabBarControllerDelegate
   - animationControllerForTransitionFrom 返回 nil 禁用系统动画
   - 使用 UIView.performWithoutAnimation 确保无动画切换

3. 修复背景色未定义导致的白色闪烁
   - 显式设置浅灰色背景作为兜底 (RGB: 0.95, 0.95, 0.97)
   - 添加背景图片的 contentMode 和 clipsToBounds 属性
   - 确保背景图片加载延迟时不显示白色

修复后效果:
- Tab 切换流畅无闪烁,仅保留按钮缩放动画
- 背景色始终一致,无白色背景闪现
- 性能提升,消除多个动画冲突
2025-10-10 15:58:23 +08:00

375 lines
13 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.

//
// EPTabBarController.swift
// YuMi
//
// Created by AI on 2025-10-09.
// Copyright © 2025 YuMi. All rights reserved.
//
import UIKit
import SnapKit
/// EP TabBar
/// + Moment Mine Tab
@objc class EPTabBarController: UITabBarController {
// MARK: - Properties
///
private var globalEventManager: GlobalEventManager?
///
private var isLoggedIn: Bool = false
/// TabBar
private var customTabBarView: UIView!
///
private var tabBarBackgroundView: UIVisualEffectView!
/// Tab
private var tabButtons: [UIButton] = []
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
//
#if DEBUG
APIConfig.testEncryption()
#endif
// TabBar
self.tabBar.isHidden = true
// delegate
self.delegate = self
setupCustomFloatingTabBar()
setupGlobalManagers()
setupInitialViewControllers()
NSLog("[EPTabBarController] 悬浮 TabBar 初始化完成")
}
deinit {
globalEventManager?.removeAllDelegates()
NSLog("[EPTabBarController] 已释放")
}
// MARK: - Setup
/// TabBar
private func setupCustomFloatingTabBar() {
//
customTabBarView = UIView()
customTabBarView.translatesAutoresizingMaskIntoConstraints = false
customTabBarView.backgroundColor = .clear
view.addSubview(customTabBarView)
// /
let effect: UIVisualEffect
if #available(iOS 26.0, *) {
// iOS 26+ 使Material
effect = UIGlassEffect()
} else {
// iOS 13-17 使
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)
// Masonry
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)
}
// Tab
setupTabButtons()
NSLog("[EPTabBarController] 悬浮 TabBar 设置完成")
}
/// Tab
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)
}
/// Tab
private func createTabButton(normalImage: String, selectedImage: String, tag: Int) -> UIButton {
let button = UIButton(type: .custom)
button.tag = tag
button.adjustsImageWhenHighlighted = false //
// 使 SF Symbols
if let normalImg = UIImage(named: normalImage), let selectedImg = UIImage(named: selectedImage) {
// normal selected
button.setImage(normalImg, for: .normal)
button.setImage(selectedImg, for: .selected)
} else {
// 使 SF Symbols
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
}
/// Tab
@objc private func tabButtonTapped(_ sender: UIButton) {
let newIndex = sender.tag
// tab
if newIndex == selectedIndex {
return
}
//
updateTabButtonStates(selectedIndex: newIndex)
// UITabBarController
UIView.performWithoutAnimation {
selectedIndex = newIndex
}
let tabNames = ["动态", "我的"]
NSLog("[EPTabBarController] 选中 Tab: \(tabNames[newIndex])")
}
/// Tab
private func updateTabButtonStates(selectedIndex: Int) {
//
tabButtons.forEach { $0.isUserInteractionEnabled = false }
for (index, button) in tabButtons.enumerated() {
let isSelected = (index == selectedIndex)
// isSelected
button.isSelected = isSelected
// SF Symbols tintColor
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 setupGlobalManagers() {
globalEventManager = GlobalEventManager.shared()
globalEventManager?.setupSDKDelegates()
// TODO: v0.2
// Build Configuration
/*
if let containerView = view {
globalEventManager?.setupRoomMiniView(on: containerView)
}
*/
//
globalEventManager?.registerSocialShareCallback()
NSLog("[EPTabBarController] 全局管理器设置完成v0.2 - 无 MiniRoom")
}
/// ViewController
private func setupInitialViewControllers() {
// TODO: 使
let blankVC1 = UIViewController()
blankVC1.view.backgroundColor = .white
blankVC1.tabBarItem = createTabBarItem(
title: "动态",
normalImage: "tab_moment_normal",
selectedImage: "tab_moment_selected"
)
let blankVC2 = UIViewController()
blankVC2.view.backgroundColor = .white
blankVC2.tabBarItem = createTabBarItem(
title: "我的",
normalImage: "tab_mine_normal",
selectedImage: "tab_mine_selected"
)
viewControllers = [blankVC1, blankVC2]
selectedIndex = 0
NSLog("[EPTabBarController] 初始 ViewControllers 设置完成")
}
/// TabBarItem
/// - Parameters:
/// - title:
/// - normalImage:
/// - selectedImage:
/// - Returns: UITabBarItem
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
/// TabBar
/// - Parameter isLogin:
func refreshTabBar(isLogin: Bool) {
isLoggedIn = isLogin
if isLogin {
setupLoggedInViewControllers()
} else {
setupInitialViewControllers()
}
NSLog("[EPTabBarController] TabBar 已刷新,登录状态: \(isLogin)")
}
/// ViewControllers
private func setupLoggedInViewControllers() {
// viewControllers
if viewControllers?.count != 2 ||
!(viewControllers?[0] is EPMomentViewController) ||
!(viewControllers?[1] is EPMineViewController) {
// ViewControllerOC
let momentVC = EPMomentViewController()
momentVC.tabBarItem = createTabBarItem(
title: "动态",
normalImage: "tab_moment_normal",
selectedImage: "tab_moment_selected"
)
let mineVC = EPMineViewController()
mineVC.tabBarItem = createTabBarItem(
title: "我的",
normalImage: "tab_mine_normal",
selectedImage: "tab_mine_selected"
)
viewControllers = [momentVC, mineVC]
NSLog("[EPTabBarController] 登录后 ViewControllers 创建完成 - Moment & Mine")
}
selectedIndex = 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? {
// nil 使
return nil
}
///
func tabBarController(_ tabBarController: UITabBarController,
shouldSelect viewController: UIViewController) -> Bool {
// nil animationController
return true
}
}
// MARK: - OC Compatibility
extension EPTabBarController {
/// OC
@objc static func create() -> EPTabBarController {
return EPTabBarController()
}
/// OC TabBar
@objc func refreshTabBarWithIsLogin(_ isLogin: Bool) {
refreshTabBar(isLogin: isLogin)
}
}