import UIKit
// MARK: - JXTabBarController
class JXTabBarController: UITabBarController {
private var customTabBarView: UIView!
private var buttons: [UIButton] = []
private var centerButton: UIButton! // ✅ 新增:凸起按钮属性
private var mibaoView: JXMiBaoView?
// Tab 配置数据
private struct TabConfig {
let normalImage: String
let selectedImage: String
let title: String
}
private let tabConfigs: [TabConfig] = [
TabConfig(normalImage: "tab_question_bank_normal", selectedImage: "tab_question_bank_select", title: ""),
TabConfig(normalImage: "tab_question_normal", selectedImage: "tab_question_select", title: ""),
TabConfig(normalImage: "tab_mine_normal", selectedImage: "tab_mine_select", title: ""),
]
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
tabBar.isHidden = true
setupViewControllers()
setupCustomTabBar()
setupCenterButton() // ✅ 新增:初始化凸起按钮
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
layoutCustomTabBar()
}
// MARK: - 设置子控制器
private func setupViewControllers() {
let homeVC = JXHomeViewController()
let questionVC = JXQuestionViewController()
let mineVC = JXMineViewController()
let homeNav = UINavigationController(rootViewController: homeVC)
let questionNav = UINavigationController(rootViewController: questionVC)
let mineNav = UINavigationController(rootViewController: mineVC)
viewControllers = [homeNav, questionNav, mineNav]
}
// MARK: - 搭建自定义 TabBar
private func setupCustomTabBar() {
let tabBarHeight: CGFloat = 49
customTabBarView = UIView()
customTabBarView.backgroundColor = .white
customTabBarView.layer.cornerRadius = 20
customTabBarView.layer.shadowColor = UIColor.black.cgColor
customTabBarView.layer.shadowOffset = CGSize(width: 0, height: -2)
customTabBarView.layer.shadowOpacity = 0.08
customTabBarView.layer.shadowRadius = 10
customTabBarView.clipsToBounds = false // ✅ 初始设为 false,防止阴影被裁
view.addSubview(customTabBarView)
for (index, config) in tabConfigs.enumerated() {
let button = UIButton(type: .custom)
button.showsTouchWhenHighlighted = false
button.backgroundColor = .clear
button.setBackgroundImage(nil, for: .normal)
button.setBackgroundImage(nil, for: .highlighted)
button.setBackgroundImage(nil, for: .selected)
button.setBackgroundImage(nil, for: [.selected, .highlighted])
button.tintColor = .clear
if let normalImg = UIImage(named: config.normalImage) {
button.setImage(normalImg.withRenderingMode(.alwaysOriginal), for: .normal)
}
if let selectedImg = UIImage(named: config.selectedImage) {
button.setImage(selectedImg.withRenderingMode(.alwaysOriginal), for: .selected)
button.setImage(selectedImg.withRenderingMode(.alwaysOriginal), for: [.selected, .highlighted])
}
button.tag = index
button.addTarget(self, action: #selector(tabButtonTapped(_:)), for: .touchUpInside)
customTabBarView.addSubview(button)
buttons.append(button)
if index == 0 {
button.isSelected = true
}
}
}
// MARK: - ✅ 新增:搭建橙色凸起按钮
private func setupCenterButton() {
centerButton = UIButton(type: .custom)
// 橙色渐变背景
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [
UIColor(red: 1.0, green: 0.65, blue: 0.2, alpha: 1.0).cgColor,
UIColor(red: 1.0, green: 0.45, blue: 0.1, alpha: 1.0).cgColor
]
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
gradientLayer.cornerRadius = 25.0 // 假设按钮尺寸为 50
centerButton.layer.insertSublayer(gradientLayer, at: 0)
centerButton.layer.cornerRadius = 25.0
centerButton.clipsToBounds = false
// 橙色光晕阴影
centerButton.layer.shadowColor = UIColor(red: 1.0, green: 0.5, blue: 0.1, alpha: 0.5).cgColor
centerButton.layer.shadowOffset = CGSize(width: 0, height: 4)
centerButton.layer.shadowOpacity = 0.6
centerButton.layer.shadowRadius = 10
centerButton.setImage(UIImage(named: "tab_upload_icon")?.withRenderingMode(.alwaysOriginal), for: .normal)
centerButton.imageView?.contentMode = .scaleAspectFit
centerButton.addTarget(self, action: #selector(centerButtonTapped), for: .touchUpInside)
// ✅ 添加到 customTabBarView 上
customTabBarView.addSubview(centerButton)
}
// MARK: - 布局
private func layoutCustomTabBar() {
let tabBarHeight: CGFloat = 49
let safeBottom = view.safeAreaInsets.bottom
let screenWidth = view.bounds.width
let centerButtonSize: CGFloat = 55.0
// 1. 布局白色卡片
customTabBarView.frame = CGRect(
x: 0,
y: view.bounds.height - tabBarHeight - safeBottom,
width: screenWidth,
height: tabBarHeight + safeBottom
)
// 2. 布局普通 Tab 按钮
let buttonWidth = screenWidth / 4
for (index, button) in buttons.enumerated() {
button.frame = CGRect(
x: CGFloat(index) * buttonWidth,
y: 0,
width: buttonWidth,
height: tabBarHeight
)
}
// ✅ 3. 布局凸起按钮(一半在里,一半在外,靠右)
let riseHeight: CGFloat = 28.0 // 向上凸出的高度(按钮高度的一半)
let rightMargin: CGFloat = 36.0 // 距离右侧的边距
centerButton.frame = CGRect(
x: screenWidth - centerButtonSize - rightMargin,
y: -riseHeight, // ✅ 负值,使其向上凸出卡片
width: centerButtonSize,
height: centerButtonSize
)
// 更新渐变层的 frame
if let gradientLayer = centerButton.layer.sublayers?.first as? CAGradientLayer {
gradientLayer.frame = centerButton.bounds
}
}
// MARK: - 点击事件
@objc private func tabButtonTapped(_ sender: UIButton) {
let index = sender.tag
buttons.forEach { $0.isSelected = false }
sender.isSelected = true
selectedIndex = index
}
// 处理发布/上传逻辑
@objc private func centerButtonTapped() {
// TODO: 处理发布/上传逻辑
guard JXUserManager.shared.checkIsLogined(self) else {
return
}
JXAddProblemViewController.show(self)
}
}
// MARK: - UIButton 扩展 - 垂直排列图标和标题
extension UIButton {
func alignVertical(spacing: CGFloat = 6.0) {
if #available(iOS 15.0, *) {
var config = configuration ?? UIButton.Configuration.plain()
config.imagePadding = spacing
config.imagePlacement = .top
config.contentInsets = NSDirectionalEdgeInsets(top: 4, leading: 0, bottom: 2, trailing: 0)
configuration = config
titleLabel?.textAlignment = .center
} else {
guard let imageSize = imageView?.image?.size,
let text = titleLabel?.text,
let font = titleLabel?.font else { return }
let textSize = text.size(withAttributes: [.font: font])
titleEdgeInsets = UIEdgeInsets(
top: 0,
left: -imageSize.width,
bottom: -(imageSize.height + spacing),
right: 0
)
imageEdgeInsets = UIEdgeInsets(
top: -(textSize.height + spacing),
left: 0,
bottom: 0,
right: -textSize.width
)
}
}
}
// MARK: - UIColor 扩展
extension UIColor {
convenience init?(hexString: String) {
var hex = hexString.trimmingCharacters(in: .whitespacesAndNewlines)
if hex.hasPrefix("#") { hex.removeFirst() }
guard hex.count == 6, let value = UInt64(hex, radix: 16) else { return nil }
self.init(
red: CGFloat((value >> 16) & 0xFF) / 255.0,
green: CGFloat((value >> 8) & 0xFF) / 255.0,
blue: CGFloat(value & 0xFF) / 255.0,
alpha: 1.0
)
}
}
extension JXTabBarController {
private func _configMibaoTipView() {
self.mibaoView?.removeFromSuperview()
self.mibaoView = nil
guard let userInfo = JXUserManager.shared.loginInfo?.userInfo else {
return
}
if let window = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first,
userInfo.secretSetFlag != true {
let view = JXMiBaoView()
window.addSubview(view)
self.mibaoView = view
view.snp.makeConstraints { make in
make.edges.equalTo(UIEdgeInsets.zero)
}
view.didSet = { [weak self] in
let viewController = JXSetSecretViewController()
viewController.modalPresentationStyle = .fullScreen
self?.present(viewController, animated: true)
}
}
}
}

image.png