具有活动圆圈的自定义TabBarController

时间:2019-01-14 09:49:30

标签: swift uitabbarcontroller

在阅读了有关自定义UITabBarControllers的几篇文章之后,我比开始研究之前更加困惑。

我的目标是创建具有3个重要属性的自定义TabBar:

  • 没有文字,只有图标
  • 活动图标由一个圆圈填充,该圆圈后面填充有颜色,因此需要不同的图标颜色

这是我要实现的目标:

Example Tab Bar

我已经能够通过遵循另一个StackOverflow答案(Remove tab bar item text, show only image)来删除文本并使图标居中,尽管该解决方案对我来说似乎是很简单的事情。

我该如何在项目后面创建一个圆圈并更改活动项目的颜色?

还有,有人介意解释XCode检查器部分“ Tab Bar Item”和“ Bar Item”之间的区别,它们之间直接相邻吗?

2 个答案:

答案 0 :(得分:1)

最简单且实际上最干净的方法是设计图标并将其作为图像导入到.xcassets文件夹中。然后,您可以使用以下命令为每个viewController的不同状态设置不同的图标:

ViewController.tabBarItem = UITabBarItem(title: "", image: yourImage.withRenderingMode(.alwaysOriginal), selectedImage: yourImage)

您选择的图像将是带有圆圈的图像,而图像将没有。它比用xcode操纵图像要容易得多,并且也便宜得多,因为编译器只需要渲染图像而不必操纵它们。

关于另一个问题UIBarItem是

  

可以添加到显示在屏幕底部的栏中的项目的抽象超类。

UITabBarItem是UIBarItem的子类,以提供额外的功能。

答案 1 :(得分:1)

第一步很简单:将UITabbarItem的title属性保留为空会隐藏标签。

您的第二步实际上可以分为两步:更改图标的颜色并在其后面添加一个圆圈。

这里的第一步又很简单:您可以为当前选择的ViewController设置一个不同的图标(我使用Storyboard,这个过程非常简单)。您要做的就是添加一个白色版本的图标,当该菜单选项被选中时将显示。

最后一步是显示圆圈。为此,我们需要以下信息:

  • 当前选择了哪个项目?
  • 图标在屏幕上的位置是什么?

这两个中的第一个很容易找到,但是第二个带来了一个问题:UITabBar中的图标在屏幕上的间距不相等,因此我们不能仅将选项卡的宽度除以数量,然后取其中一半找到图标的中心。相反,我们将把UITabBarController子类化。

  

注意:UITabBarController的tabBar属性确实具有.selectionIndicatorImage属性。您可以为此分配一个图像,该图像将显示在图标后面。但是,您无法轻松控制此图像的位置,因此这就是为什么我们仍然要继承UITabBarController的原因。

class CircledTabBarController: UITabBarController {

    var circle: UIView?

    override func viewDidLoad() {
        super.viewDidLoad()
        let numberOfItems = CGFloat(tabBar.items!.count)
        let tabBarItemSize = CGSize(width: (tabBar.frame.width / numberOfItems) - 20, height: tabBar.frame.height)
        circle = UIView(frame: CGRect(x: 0, y: 0, width: tabBarItemSize.height, height: tabBarItemSize.height))
        circle?.backgroundColor = .darkGray
        circle?.layer.cornerRadius = circle!.frame.width/2
        circle?.alpha = 0
        tabBar.addSubview(circle!)
        tabBar.sendSubview(toBack: circle!)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let index = -(tabBar.items?.index(of: tabBar.selectedItem!)?.distance(to: 0))!
        let frame = frameForTabAtIndex(index: index)
        circle?.center.x = frame.origin.x + frame.width/2
        circle?.alpha = 1
    }

    override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
        let index = -(tabBar.items?.index(of: item)?.distance(to: 0))!
        let frame = frameForTabAtIndex(index: index)
        self.circle?.center.x = frame.origin.x + frame.width/2
    }

    func frameForTabAtIndex(index: Int) -> CGRect {
        var frames = tabBar.subviews.compactMap { (view:UIView) -> CGRect? in
            if let view = view as? UIControl {
                for item in view.subviews {
                    if let image = item as? UIImageView {
                        return image.superview!.convert(image.frame, to: tabBar)
                    }
                }
                return view.frame
            }
            return nil
        }
        frames.sort { $0.origin.x < $1.origin.x }
        if frames.count > index {
            return frames[index]
        }
        return frames.last ?? CGRect.zero
    }

}

现在使用UITabBarController的这个子类代替基类。

那么,为什么只将图标更改为带圆圈的图标呢?因为您可以用这做很多不同的事情。 I wrote an article about animating the UITabBarController in a similar manner,并且,如果您愿意,也可以轻松使用上述实现向自己的动画添加动画。