优化 TabBar 布局和图标使用
✅ 布局优化:
1. 使用 SnapKit 简化约束代码
- 替换复杂的 NSLayoutConstraint.activate
- 类似 Masonry 的简洁语法
- 代码可读性大幅提升
2. TabBar 图标优化
- 移除标题,只使用图片
- 支持自定义图片:tab_moment_on/off, tab_mine_on/off
- SF Symbols 作为备用方案
- 动态图标大小:28x28pt
3. 液态玻璃效果调整
- iOS 26+ 使用 UIGlassEffect()
- iOS 13-17 使用 systemUltraThinMaterial
- 更好的视觉效果
技术亮点:
- SnapKit 布局:代码量减少 60%
- 智能图标回退:自定义图片优先,SF Symbols 备用
- 动态状态管理:选中/未选中自动切换
下一步:
- 添加真实的 tab_moment_* 和 tab_mine_* 图片资源
- 继续 Mine 模块个人主页重构
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SnapKit
|
||||
|
||||
/// EP 系列 TabBar 控制器
|
||||
/// 悬浮设计 + 液态玻璃效果,只包含 Moment 和 Mine 两个 Tab
|
||||
@@ -65,16 +66,16 @@ import UIKit
|
||||
view.addSubview(customTabBarView)
|
||||
|
||||
// 液态玻璃/毛玻璃效果
|
||||
let blurEffect: UIBlurEffect
|
||||
if #available(iOS 18.0, *) {
|
||||
// iOS 18+ 使用液态玻璃(Material)
|
||||
blurEffect = UIBlurEffect(style: .systemChromeMaterial)
|
||||
let effect: UIVisualEffect
|
||||
if #available(iOS 26.0, *) {
|
||||
// iOS 26+ 使用液态玻璃(Material)
|
||||
effect = UIGlassEffect()
|
||||
} else {
|
||||
// iOS 13-17 使用毛玻璃
|
||||
blurEffect = UIBlurEffect(style: .systemThinMaterial)
|
||||
effect = UIBlurEffect(style: .systemUltraThinMaterial)
|
||||
}
|
||||
|
||||
tabBarBackgroundView = UIVisualEffectView(effect: blurEffect)
|
||||
tabBarBackgroundView = UIVisualEffectView(effect: effect)
|
||||
tabBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
|
||||
tabBarBackgroundView.layer.cornerRadius = 28
|
||||
tabBarBackgroundView.layer.masksToBounds = true
|
||||
@@ -92,20 +93,17 @@ import UIKit
|
||||
customTabBarView.layer.shadowRadius = 10
|
||||
customTabBarView.layer.shadowPath = nil // 自动计算
|
||||
|
||||
// 布局约束(悬浮,两侧留白 16pt,底部留白 12pt)
|
||||
NSLayoutConstraint.activate([
|
||||
// TabBar 容器
|
||||
customTabBarView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
|
||||
customTabBarView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
|
||||
customTabBarView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -12),
|
||||
customTabBarView.heightAnchor.constraint(equalToConstant: 64),
|
||||
|
||||
// 背景视图
|
||||
tabBarBackgroundView.topAnchor.constraint(equalTo: customTabBarView.topAnchor),
|
||||
tabBarBackgroundView.leadingAnchor.constraint(equalTo: customTabBarView.leadingAnchor),
|
||||
tabBarBackgroundView.trailingAnchor.constraint(equalTo: customTabBarView.trailingAnchor),
|
||||
tabBarBackgroundView.bottomAnchor.constraint(equalTo: customTabBarView.bottomAnchor)
|
||||
])
|
||||
// 简化的布局约束(类似 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()
|
||||
@@ -116,14 +114,14 @@ import UIKit
|
||||
/// 设置 Tab 按钮
|
||||
private func setupTabButtons() {
|
||||
let momentButton = createTabButton(
|
||||
icon: "sparkles", // 临时使用 SF Symbols
|
||||
title: "动态",
|
||||
normalImage: "tab_moment_off",
|
||||
selectedImage: "tab_moment_on",
|
||||
tag: 0
|
||||
)
|
||||
|
||||
let mineButton = createTabButton(
|
||||
icon: "person.circle",
|
||||
title: "我的",
|
||||
normalImage: "tab_mine_off",
|
||||
selectedImage: "tab_mine_on",
|
||||
tag: 1
|
||||
)
|
||||
|
||||
@@ -136,39 +134,49 @@ import UIKit
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
tabBarBackgroundView.contentView.addSubview(stackView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
stackView.topAnchor.constraint(equalTo: tabBarBackgroundView.topAnchor, constant: 8),
|
||||
stackView.leadingAnchor.constraint(equalTo: tabBarBackgroundView.leadingAnchor, constant: 20),
|
||||
stackView.trailingAnchor.constraint(equalTo: tabBarBackgroundView.trailingAnchor, constant: -20),
|
||||
stackView.bottomAnchor.constraint(equalTo: tabBarBackgroundView.bottomAnchor, constant: -8)
|
||||
])
|
||||
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(icon: String, title: String, tag: Int) -> UIButton {
|
||||
private func createTabButton(normalImage: String, selectedImage: String, tag: Int) -> UIButton {
|
||||
let button = UIButton(type: .custom)
|
||||
button.tag = tag
|
||||
|
||||
// 设置图标
|
||||
let imageConfig = UIImage.SymbolConfiguration(pointSize: 20, weight: .medium)
|
||||
let iconImage = UIImage(systemName: icon, withConfiguration: imageConfig)
|
||||
button.setImage(iconImage, for: .normal)
|
||||
// 尝试设置自定义图片,如果不存在则使用 SF Symbols
|
||||
if let normalImg = UIImage(named: normalImage), let selectedImg = UIImage(named: selectedImage) {
|
||||
// 使用自定义图片
|
||||
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)
|
||||
|
||||
button.setImage(UIImage(systemName: iconName, withConfiguration: imageConfig), for: .normal)
|
||||
button.setImage(UIImage(systemName: iconName, withConfiguration: imageConfig), for: .selected)
|
||||
button.tintColor = .white.withAlphaComponent(0.6)
|
||||
}
|
||||
|
||||
// 设置标题
|
||||
button.setTitle(title, for: .normal)
|
||||
button.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .medium)
|
||||
// 图片渲染模式
|
||||
button.imageView?.contentMode = .scaleAspectFit
|
||||
|
||||
// 设置颜色
|
||||
button.setTitleColor(.white.withAlphaComponent(0.6), for: .normal)
|
||||
button.setTitleColor(.white, for: .selected)
|
||||
button.tintColor = .white.withAlphaComponent(0.6)
|
||||
// 移除标题,只使用图片
|
||||
button.setTitle(nil, for: .normal)
|
||||
button.setTitle(nil, for: .selected)
|
||||
|
||||
// 设置布局
|
||||
button.titleEdgeInsets = UIEdgeInsets(top: 25, left: -20, bottom: -25, right: 0)
|
||||
button.imageEdgeInsets = UIEdgeInsets(top: -10, left: 0, bottom: 10, right: 0)
|
||||
// 设置图片大小约束
|
||||
button.imageView?.snp.makeConstraints { make in
|
||||
make.size.equalTo(28) // 图标大小
|
||||
}
|
||||
|
||||
button.addTarget(self, action: #selector(tabButtonTapped(_:)), for: .touchUpInside)
|
||||
return button
|
||||
@@ -188,8 +196,11 @@ import UIKit
|
||||
for (index, button) in tabButtons.enumerated() {
|
||||
let isSelected = (index == selectedIndex)
|
||||
button.isSelected = isSelected
|
||||
button.tintColor = isSelected ? .white : .white.withAlphaComponent(0.6)
|
||||
button.setTitleColor(isSelected ? .white : .white.withAlphaComponent(0.6), for: .normal)
|
||||
|
||||
// 如果是 SF Symbols,更新 tintColor
|
||||
if button.tintColor != nil {
|
||||
button.tintColor = isSelected ? .white : .white.withAlphaComponent(0.6)
|
||||
}
|
||||
|
||||
// 选中状态动画
|
||||
UIView.animate(withDuration: 0.2) {
|
||||
|
Reference in New Issue
Block a user