Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to add indicator under tab bar buttons in iOS #288

Open
onmyway133 opened this issue May 27, 2019 · 0 comments
Open

How to add indicator under tab bar buttons in iOS #288

onmyway133 opened this issue May 27, 2019 · 0 comments

Comments

@onmyway133
Copy link
Owner

onmyway133 commented May 27, 2019

selectionIndicatorImage

Use this property to specify a custom selection image. Your image is rendered on top of the tab bar but behind the contents of the tab bar item itself. The default value of this property is nil, which causes the tab bar to apply a default highlight to the selected item

Custom UITabBar or UITabBarController

  • Hide existing tabBar tabBar.isHidden = true
  • Position custom buttons
import UIKit

class CustomTabBarController: UITabBarController {
    private let bar = UIView()
    private var buttons = [UIButton]()

    override var viewControllers: [UIViewController]? {
        didSet {
        	populate(viewControllers: viewControllers)
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        setup()
    }

    private func setup() {
        tabBar.isHidden = true
        bar.backgroundColor = R.color.background
        view.addSubview(bar)
        NSLayoutConstraint.on([
            bar.leftAnchor.constraint(equalTo: view.leftAnchor),
            bar.rightAnchor.constraint(equalTo: view.rightAnchor),
            bar.bottomAnchor.constraint(equalTo: view.bottomAnchor),
			bar.topAnchor.constraint(equalTo: tabBar.topAnchor)
        ])
    }

    private func populate(viewControllers: [UIViewController]) {
        buttons.forEach {
            $0.removeFromSuperview()
        }

        buttons = viewControllers.map({
            let button = UIButton()
            button.setImage($0.tabBarItem.image, for: .normal)
            bar.addSubview(button)
            return button
        })

        view.setNeedsLayout()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        let padding: CGFloat = 20
        let buttonSize = CGSize(width: 30, height: 44)
        let width = view.bounds.width - padding * 20

        for (index, button) in buttons.enumerated() {
            button.center = CGPoint(x: bar.center.x, y: bar.frame.height/2)
        }
    }
}

Handle UITabBarControllerDelegate

  • Override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem)
  • tabBar.subviews contains 1 private UITabBarBackground and many private UITabBarButton
import UIKit

class CustomTabBarController: UITabBarController {
    let indicator: UIView = {
        let view = UIView()
        view.backgroundColor = R.color.primary
        view.frame.size = CGSize(width: 32, height: 2)
        view.layer.cornerRadius = 1

        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        tabBar.addSubview(indicator)
        self.delegate = self
    }

    private func animate(index: Int) {
        let buttons = tabBar.subviews
            .filter({ String(describing: $0).contains("Button") })

        guard index < buttons.count else {
            return
        }

        let selectedButton = buttons[index]
        UIView.animate(
            withDuration: 0.25,
            delay: 0,
            options: .curveEaseInOut,
            animations: {
                let point = CGPoint(
                    x: selectedButton.center.x,
                    y: selectedButton.frame.maxY - 1
                )

                self.indicator.center = point
            },
            completion: nil
        )
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        animate(index: selectedIndex)
    }
}

extension CustomTabBarController: UITabBarControllerDelegate {
    override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
        guard
            let items = tabBar.items,
            let index = items.firstIndex(of: item)
        else {
            return
        }

        animate(index: index)
    }
}

In iOS 13, we need to use viewDidAppear

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    moveIndicator(index: selectedIndex, animated: false)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant