前言:
第一次用Swift学习写一个自定义Tabbar,不知道理的清楚不。不同的代码敲的还有点小乱,慢慢磨吧。
目录:
001 - 自定义TabBar大概过程
002 - 核心代码
自定义TabBar步骤
1.创建一个tabBar控制器,把他作为窗口的根控制,并显示。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = LLTabBarViewController() window?.backgroundColor = UIColor.white window?.makeKeyAndVisible() return true }
效果图:
2.我们自定义一个继承于UITabBar的Tabbar类,并利用KVC将系统的Tabbar替换掉。
import UIKit class ZBTabBarController: UITabBarController { var TabBar = ZBTabBar() override func viewDidLoad() { super.viewDidLoad() self.creatTabBar() } func creatTabBar() { let customTabBar: ZBTabBar = ZBTabBar() self.setValue(customTabBar, forKey: "tabBar") TabBar = customTabBar } }
3.根据标题数量,便利添加子控制器,并配置其基本信息。
func setRootTaBarVc() { var Vc: UIViewController? for i in 0 ..< self.tabBarTitles.count { print(self.tabBarTitles[i]) switch i { case 0: Vc = ZBInkDropController() case 1: Vc = ZBExchangeController() case 2: Vc = ZBTreasureHuntController() case 3: Vc = ZBMineController() default: break } // 1.创建导航控制器 let nav = ZBRootNavController.init(rootViewController: Vc!) // 2.创建tabbarItem let barItem = UITabBarItem.init(title: self.tabBarTitles[i], image: UIImage.init(named: self.tabBarNormalImgs[i])?.withRenderingMode(.alwaysOriginal), selectedImage: UIImage.init(named: self.tabBarSelectImgs[i])?.withRenderingMode(.alwaysOriginal)) // 3.更改字体颜色 barItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor:RGBAlpa(128,128,128,1)], for:.normal ) barItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor:RGBAlpa(0,0,0,1)], for: .selected) // 设置标题 Vc?.title = self.tabBarTitles[i] // 设置根控制器 Vc?.tabBarItem = barItem // 添加到当前控制器 self.addChild(nav) } } }
效果图如下:
4.根据标题数量,便利添加子控制器,并配置其基本信息
class ZBTabBar: UITabBar { // 懒加载创建一个中间按钮 private lazy var addButton:UIButton = { return UIButton() }() // 视图初始化 override init(frame: CGRect) { super.init(frame: frame) addButton.setBackgroundImage(UIImage.init(named: "tabbar_margin_img"), for: .normal) addButton.addTarget(self, action: #selector(ZBTabBar.addButtonClick), for: .touchUpInside) self.addSubview(addButton) self.backgroundImage = UIColor.creatImageWithColor(color: UIColor.white) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // 点击中间按钮的实现方法 @objc func addButtonClick() { print("我点击了中间的按钮") } // 调用布局约束方法 override func layoutSubviews() { super.layoutSubviews() let buttonX = SCREEN_WIDTH / 5 var index = 0 for barButton in self.subviews { if barButton.isKind(of: NSClassFromString("UITabBarButton")!) { if index == 2 { /// 设置添加按钮位置 addButton.frame.size = CGSize.init(width: (addButton.currentBackgroundImage?.size.width)!, height: (addButton.currentBackgroundImage?.size.height)!) addButton.center = CGPoint.init(x: self.center.x, y: self.frame.size.height/2 - 15) index += 1 } barButton.frame = CGRect.init(x: buttonX * CGFloat(index), y: 0, width: buttonX, height: self.frame.size.height) index += 1 } } self.bringSubviewToFront(addButton) } }
效果图如下:
5.接下来,我们会发现,点击中间按钮超出父类的部分不能点击,所以我们做的是重写hitTest方法,监听按钮的点击 让凸出tabbar的部分响应点击。
/// 重写hitTest方法,监听按钮的点击 让凸出tabbar的部分响应点击 override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { // 判断是否为根控制器 if self.isHidden { // tabbar隐藏 不在主页 系统处理 return super.hitTest(point, with: event) }else { // 将单线触摸点转换到按钮上生成新的点 let onButton = self.convert(point, to: self.addButton) // 判断新的点是否在按钮上 if self.addButton.point(inside: onButton, with: event) { return addButton }else { // 不再按钮上 系统处理 return super.hitTest(point, with: event) } }
6.最后,我们创建一个代理事件,让按钮的点击由控制器去代理,执行点击事件。
protocol RootTabBarDelegate :NSObjectProtocol{ func addClick() } // 点击中间按钮的实现方法 @objc func addButtonClick() { if addDelegate != nil{ addDelegate?.addClick() } }
7.tabbar控制器,设置、遵守代理就ok了。
核心代码
ZBTabBarController.swift
class ZBTabBarController: UITabBarController,RootTabBarDelegate { // 点击中间按钮的方法 func addClick() { print("点击中间按钮的方法") } var TabBar = ZBTabBar() var tabBarNormalImgs = ["1","2","3","4"] var tabBarSelectImgs = ["1","2","3","4"] var tabBarTitles = ["墨滴","兑换","寻宝","我的"] override func viewDidLoad() { super.viewDidLoad() self.creatTabBar() } func creatTabBar() { // 替换系统Tabbar let customTabBar: ZBTabBar = ZBTabBar() customTabBar.addDelegate = self self.setValue(customTabBar, forKey: "tabBar") TabBar = customTabBar // 设置tabbar子控制器 self.setRootTaBarVc() } func setRootTaBarVc() { var Vc: UIViewController? for i in 0 ..< self.tabBarTitles.count { print(self.tabBarTitles[i]) switch i { case 0: Vc = ZBInkDropController() case 1: Vc = ZBExchangeController() case 2: Vc = ZBTreasureHuntController() case 3: Vc = ZBMineController() default: break } // 1.创建导航控制器 let nav = ZBRootNavController.init(rootViewController: Vc!) // 2.创建tabbarItem let barItem = UITabBarItem.init(title: self.tabBarTitles[i], image: UIImage.init(named: self.tabBarNormalImgs[i])?.withRenderingMode(.alwaysOriginal), selectedImage: UIImage.init(named: self.tabBarSelectImgs[i])?.withRenderingMode(.alwaysOriginal)) // 3.更改字体颜色 barItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor:RGBAlpa(128,128,128,1)], for:.normal ) barItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor:RGBAlpa(0,0,0,1)], for: .selected) // 设置标题 Vc?.title = self.tabBarTitles[i] // 设置根控制器 Vc?.tabBarItem = barItem // 添加到当前控制器 self.addChild(nav) } } }
ZBTabBar.swift
import UIKit protocol RootTabBarDelegate :NSObjectProtocol{ func addClick() } class ZBTabBar: UITabBar { weak var addDelegate: RootTabBarDelegate? // 懒加载创建一个中间按钮 private lazy var addButton:UIButton = { return UIButton() }() // 视图初始化 override init(frame: CGRect) { super.init(frame: frame) addButton.setBackgroundImage(UIImage.init(named: "tabbar_margin_img"), for: .normal) addButton.addTarget(self, action: #selector(ZBTabBar.addButtonClick), for: .touchUpInside) self.addSubview(addButton) self.backgroundImage = UIColor.creatImageWithColor(color: UIColor.white) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } // 点击中间按钮的实现方法 @objc func addButtonClick() { if addDelegate != nil{ addDelegate?.addClick() } } // 调用布局约束方法 override func layoutSubviews() { super.layoutSubviews() let buttonX = SCREEN_WIDTH / 5 var index = 0 for barButton in self.subviews { if barButton.isKind(of: NSClassFromString("UITabBarButton")!) { if index == 2 { /// 设置添加按钮位置 addButton.frame.size = CGSize.init(width: (addButton.currentBackgroundImage?.size.width)!, height: (addButton.currentBackgroundImage?.size.height)!) addButton.center = CGPoint.init(x: self.center.x, y: self.frame.size.height/2 - 15) index += 1 }
barButton.frame = CGRect.init(x: buttonX * CGFloat(index), y: 0, width: buttonX, height: self.frame.size.height) index += 1 } } self.bringSubviewToFront(addButton) } /// 重写hitTest方法,监听按钮的点击 让凸出tabbar的部分响应点击 override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { // 判断是否为根控制器 if self.isHidden { // tabbar隐藏 不在主页 系统处理 return super.hitTest(point, with: event) }else { // 将单线触摸点转换到按钮上生成新的点 let onButton = self.convert(point, to: self.addButton) // 判断新的点是否在按钮上 if self.addButton.point(inside: onButton, with: event) { return addButton }else { // 不再按钮上 系统处理 return super.hitTest(point, with: event) } } } }