Swift iOS 15.0 以后自定义Tabbar, 运行效果可以看程序员面试指南

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
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容