阅读 90

可动态化配置的中央按钮凸起的UITabBarController

最近快要过节了,所以项目中有一个需求,四个按钮的image通过后台返回的图片来进行配置。同时中间的凸起按钮是否展现也通过后台来配置。

自定义TabBar类

因为项目比较急,以前没有中间凸起按钮的设计,所以首先考虑实现的是新建一个继承UITaBar的类,然后通过valueForKeyPath来替换系统的tabbar。

自定义WWTabBar

class WWTabBar: UITabBar {
        var oldSafeAreaInsets = UIEdgeInsets.zero
        // tabbar高度49
        let TabbarHeight: CGFloat = 49.0
        let centerWidth: CGFloat = 62.0
        let centerHeight: CGFloat = 48.0

        // 中间按钮
        public var centerBtn: UIButton = UIButton(type: .custom)
        // 中间按钮图片url
        var centerImageUrl: String? {
            didSet {
                        centerBtn.kf.setImage(with: URL(string: centerImageUrl ?? ""), for: .normal)
            }
        }

        override init(frame: CGRect) {
            super.init(frame: frame)
            centerBtn.adjustsImageWhenHighlighted = false
            addSubview(centerBtn)
        }

        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }

        override func layoutSubviews() {
            super.layoutSubviews()

            centerBtn.frame = CGRect(x: (UIScreen.main.bounds.size.width - centerWidth) / 2, y: -5, width: centerWidth, height: centerHeight)

            //系统自带的按钮类型是UITabBarButton,找出这些类型的按钮,然后重新排布位置,空出中间的位置
            let btnWidth = self.frame.size.width / 5
            for btn in self.subviews {
                if btn.isKind(of: NSClassFromString("UITabBarButton")!.self) {
                    let label = btn.value(forKeyPath: "_label") as! UILabel

                    switch (label.text ?? "") {
                    case "首页":
                        btn.frame = CGRect(x: 0,
                                y: 0,
                                width: btnWidth,
                                height: TabbarHeight)

                    case "探索":
                        btn.frame = CGRect(x: btnWidth,
                                y: 0,
                                width: btnWidth,
                                height: TabbarHeight)
                    case "发现":
                        btn.frame = CGRect(x: btnWidth * 3,
                                y: 0,
                                width: btnWidth,
                                height: TabbarHeight)


                    case "我的":
                        btn.frame = CGRect(x: btnWidth * 4,
                                y: 0,
                                width: btnWidth,
                                height: TabbarHeight)
                    default:
                        return
                    }


                }

            }
        }

        // 处理超出tabbar部分按钮点击无效的问题
        override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
            if !self.isHidden {
                // 转换坐标
                let tempPoint = centerBtn.convert(point, from: self)
                // 判断点击的点是否在按钮区域内
                if centerBtn.bounds.contains(tempPoint) {
                    // 返回按钮
                    return centerBtn
                }
            }
            return super.hitTest(point, with: event)
        }
    }
复制代码

动态配置中间按钮,自定义UITabBarController。如果配置中间按钮,使用WWTabar替换,若不配置,使用默认TabBar。 为了扩展性,我们先定义一个WWTabBarController

class WWTabBarController: UITabBarController {
    var isShow = false
    var tabIcon: UIImage?
    var tabIconUrl: String?
    var toUrl: String?
    var customTabbar: WWTabBar?

    init(isShowPlus: Bool, icon: UIImage?, iconUrl: String?, uri: String?) {
        isShow = isShowPlus
        tabIcon = icon
        tabIconUrl = iconUrl
        toUrl = uri
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        if isShow {
            customTabbar = WWTabBar(frame: .zero)
            setValue(customTabbar, forKeyPath: "tabBar")
            customTabbar?.centerBtn.addTarget(self, action: #selector(centerBtnAction), for: .touchUpInside)
        }
    }

    // 中间按钮点击
    @objc func centerBtnAction() {
//        LocalRouter.shared.open(url: toUrl ?? "")
    }
}
复制代码

这里有中间按钮的图片、跳转链接等等,通过isShow属性来决定是否用自定义tabbar来替换系统的。中间按钮路径跳转到toUrl,这里需要自己的路由实现。 使用时,新建类来继承这个WWTabBarController

class TestTabBarController: WWTabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()
        addChildViewControllers()
    }

    //添加子控制器
    func addChildViewControllers() {
        addChildViewController(ViewController(), title: "首页", imageName: "tab1")
        addChildViewController(ViewController(), title: "探索", imageName: "tab2")
        addChildViewController(ViewController(), title: "发现", imageName: "tab3")
        addChildViewController(ViewController(), title: "我的", imageName: "tab4")
    }

    // 添加子vc
    func addChildViewController(_ childController: UIViewController, title: String, imageName: String) {
        childController.title = title
        if imageName.count > 0{
            childController.tabBarItem.image = UIImage(named: imageName)
            childController.tabBarItem.selectedImage = UIImage(named: imageName)
        }
        let nav = UINavigationController(rootViewController: childController)
        addChild(nav)
    }
}
复制代码

在这里配置四个tabbaritem和对应的页面 使用时,从后台拿到配置,然后进行初始化

使用系统tababr

self.window?.rootViewController = TestTabBarController(isShowPlus: false, icon: nil, iconUrl: nil, uri: nil)
复制代码

添加中间凸起按钮

self.window?.rootViewController = TestTabBarController(isShowPlus: true, icon: image, iconUrl: "iconurl", uri: "yoururl")
复制代码

因为可能会遇见网络不好的情况。所以项目一开始使用系统的TabBar,请求配置文件并保存下来。第二次再使用本地的配置文件来初始化。所以我建议不要直接使用URl来加载图片,将图片down下来然后设置到TabBar中。在上面的WWTabBar中我是设置的url,需要修改一下。

由于我们项目中首页对应的TabBarItem Icon是会变化的,再滑动一定距离后,首页Icon会变成刷新图标,所以会再次调用layoutSubviews方法,这个layout过程会被用户看见,所以这个方法不可行。所以尝试自定义View,不继承系统UITabBar。 这里先挖个坑。如果没有这个需求,可以用上述方法。 github.com/FrunkPiano/…

最近加了一些iOS开发相关的QQ群和微信群,但是感觉都比较水,里面对于技术的讨论比较少,所以自己建了一个iOS开发进阶讨论群,欢迎对技术有热情的同学扫码加入,加入以后你可以得到:

  1. 技术方案的讨论,会有在大厂工作的高级开发工程师尽可能抽出时间给大家解答问题
  2. 每周定期会写一些文章,并且转发到群里,大家一起讨论,也鼓励加入的同学积极得写技术文章,提升自己的技术
  3. 如果有想进大厂的同学,里面的高级开发工程师也可以给大家内推,并且针对性得给出一些面试建议 群已经满100人了,想要加群的小伙伴们可以扫码加这个微信,备注:“加群+昵称”,拉你进群,谢谢了

关注下面的标签,发现更多相似文章
评论