代码之家  ›  专栏  ›  技术社区  ›  NikxDa

带活动圆的自定义TabbarController

  •  1
  • NikxDa  · 技术社区  · 7 年前

    在阅读了一些关于定制的uitababarcontrollers的文章之后,我甚至比一开始做研究之前更加困惑。

    我的目标是创建一个具有三个重要属性的自定义选项卡:

    • 没有文本,只有图标
    • 活动图标由其后面填充颜色的圆圈标记,因此需要不同的图标颜色。

    以下是我想要达到的目标:

    Example Tab Bar

    我已经能够删除文本,并按照另一个stackoverflow答案将图标居中( Remove tab bar item text, show only image ,尽管我觉得这个解决方案像是一个黑客。

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

    另外,是否有人介意解释Xcode检查器部分“标签栏项目”和“栏项目”之间的区别,它们直接出现在彼此的下面?

    2 回复  |  直到 7 年前
        1
  •  1
  •   NikxDa    7 年前

    第一步很简单:将uitabaritem的title属性留空将隐藏标签。

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

    这里的第一步又很简单:您可以为当前选定的视图控制器设置不同的图标(我使用故事板,这个过程非常简单)。你要做的是添加一个白色的图标,当菜单选项被选中时显示出来。

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

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

    这两个图标中的第一个很容易找到,但第二个却带来了一个问题:UItaBar中的图标在屏幕上的间距不是相等的,所以我们不能将Tabbar的宽度除以其中的项目数量,然后取其中的一半来找到图标的中心。相反,我们将子类UItabarController。

    注: 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
        }
    
    }
    

    现在使用uitabarcontroller的这个子类,而不是基类。

    那么,为什么这种方法会简单地将图标改为带圆圈的图标呢?因为你可以用这个做很多不同的事情。 I wrote an article about animating the UITabBarController in a similar manner 如果您愿意,也可以轻松地使用上面的实现向您的实现中添加动画。

        2
  •  1
  •   Galo Torres Sevilla    7 年前

    最简单和最干净的方法是设计图标并将其作为图像导入到.xcassets文件夹。然后,您只需为每个视图控制器的不同状态设置不同的图标:

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

    所选图像将是带圆圈的图像,而图像将不带圆圈。它比在Xcode中操作图像容易得多,而且成本也更低,因为编译器只需要渲染图像,而不必操作它们。

    另一个问题是

    一个抽象超类,用于可添加到显示在屏幕底部的栏中的项。

    uitabaritem是uibaritem的一个子类,用于提供额外的功能。

    推荐文章