当前位置: 首页 > news >正文

艺术成分很高的完全自定义的UITabBar(很简单)

引言

在iOS应用开发中,UITabBar是一个非常场景且重要的UI组件。系统为我们提供的UITabBar虽然功能强大,但是在某些情况下,它的标准样式并不能满足我们特定的设计需求,它的灵活性也有一些局限。为了打造更具个性化好的用户友好的交互,我们十分有必要了解该如何自定义UITabBar。

这篇博客我们将探讨如何通过代码来定制UITabBar,如果修改外观,如何添加动画,以及更加复杂的交互等等,希望能够帮助大家打造更加独特更加吸引人的iOS应用页面。

系统UITabBar的痛点

默认的UITabBar虽然创建的过程可以为我们省略很多代码,但是它的缺点也十分明显,有时候我们甚至需要写更多的代码来弥补它的不足,还有时候我们甚至对它束手无策。

  1. 定制型有限:首先它的外观和布局是固定,几乎无法满足任何特定的设计需求,比如最常见的凸出式的Tab按钮。
  2. 动画效果欠缺:系统为我们提供的UITabBar缺乏炫酷的动画效果,如果想要实现自定义的过渡动画和交互可能需要写大量的代码。
  3. 扩展性比较差:通常我们还会为按钮添加很多特别的标签,比如消息数量,新功能上线小红点,或者是用户徽章等,如果在系统提供的UITabBar的按钮上添加会非常麻烦。
  4. 交互设计局限:系统的UITabBar在交互设计上十分简单,只有点击事件,如果想要增加长按或者双击或者其他交互并不容易。
  5. 页面切换管理麻烦:当我们切换到一个不需要TabBar的页面还需要自己来管理它的隐藏和显示,有的时候动画显得不流畅。

自定义UITabBar

要解决系统UITabBar的各种痛点,我们需要进行自定义。这通常包括更改外观、添加动画效果、动态管理标签、扩展功能以及优化响应式设计。在自定义UITabBar时,常见的做法是继承UITabBar类,利用其现有的方法和属性来进行扩展和修改。

然而,为了最大限度地提升灵活性和可扩展性,我们可以直接继承UIView来创建自己的TabBar。这样可以完全掌控TabBar的布局和行为,从而更灵活地实现定制需求。

接下来我们将展示如何通过继承UIView来创建一个自定义的TabBar。通过这种方法,我们可以:

  1. 完全自定义外观:任意修改TabBar的形状、颜色、大小和背景。
  2. 添加自定义动画:为选项卡切换添加炫酷的过渡动画和交互效果。
  3. 扩展功能:在TabBar中添加通知徽章、特殊按钮或其他自定义控件。
  4. 丰富交互设计:实现复杂的交互效果,例如双击切换标签、长按弹出菜单等。

准备工作

为了让继承自UIView的TabBar生效,首先我们要自定义UITabbarController,不过这并不需要大费周章,因为我们的目的仅仅是为了隐藏它自带的系统TabBar。

我们就继承自UITabbarController创建一个名为CSCustomTabBarVC的子类,并且通过从写它的viewDidLoad方法来隐藏系统的UITabBar具体代码如下:

    override func viewDidLoad() {super.viewDidLoad()self.view.backgroundColor = .whitehiddenRealTabbar()}//MARK: 隐藏tabbarprivate func hiddenRealTabbar() {for view in view.subviews {if view.isKind(of: UITabBar.self) {view.isHidden = truebreak}}}

完全自定义外观

开始自定义TabBar,继承自UIView创建了一个名为CSTabbarView的类,我们开始来为它自定义外观,就按照上面的案例为它添加5个按钮,(注意虽然它不需要和你的页面对应,但至少要和功能对应奥)。

class CSTabbarView: UIView {/// 当前选中buttonvar selectedButton:UIButton? = nil/// 渐变var gradientView = CLGradientView(startColor: UIColor.white.withAlphaComponent(0), endColor:UIColor.white.withAlphaComponent(1.0), direction: .topToBottom)/// 背景var backView = UIView()/// 第一个按钮let firstButton = UIButton()/// 第二个按钮let secondButton = UIButton()/// 开播按钮let startLiveButton = UIButton()/// 第三个按钮let thirdButton = UIButton()/// 第四个按钮let fourthButton = UIButton()
}

为了让代码更直观,我们将所有的按钮都列举了出来,当然我们可以通过数据循环去创建这些按钮(通常来讲,通过数据来创建会更灵活一些)。

这里还为TabBar添加了一个渐变的背景颜色,具体代码就不贴出了采用的是特殊图层CAGradientLayer,关于这个图层在核心动画专栏中也有过介绍。

接下来就来添加和布局这些视图和按钮,具体代码如下:

    func setupView() {// 渐变self.addSubview(gradientView)// 背景self.addSubview(backView)backView.backgroundColor = .whitebackView.layer.cornerRadius = (cs_tabbarHeight-cs_bottomInset)/2.0backView.layer.shadowOffset = CGSize(width: 0, height: 3)backView.layer.shadowColor = UIColor.black.withAlphaComponent(0.1).cgColorbackView.layer.shadowOpacity = 1backView.layer.shadowRadius = 40// 第一个按钮backView.addSubview(firstButton)firstButton.tag = 100// 第二个按钮backView.addSubview(secondButton)secondButton.tag = 101// 直播按钮self.addSubview(startLiveButton)startLiveButton.setImage(UIImage(named: "tabbar_broadcast_icon"), for: .normal)// 第三个按钮backView.addSubview(thirdButton)thirdButton.tag = 102// 第四个按钮backView.addSubview(fourthButton)fourthButton.tag = 103}

布局代码:

    func setLayout() {// 渐变gradientView.snp.makeConstraints { make inmake.leading.trailing.equalToSuperview()make.bottom.equalToSuperview()make.top.equalToSuperview()}// 背景backView.snp.makeConstraints { make inmake.leading.equalToSuperview().offset(16.0)make.trailing.equalToSuperview().offset(-16.0)make.bottom.equalToSuperview().offset(-cs_bottomInset)make.height.equalTo(cs_tabbarHeight-cs_bottomInset)}let padding = (CS_SCREENWIDTH - 32 * 2.0 - 16 * 2.0 - 32 * 5.0) / 4.0// 第一个按钮firstButton.snp.makeConstraints { make inmake.leading.equalToSuperview().offset(32.0)make.centerY.equalToSuperview()make.size.equalTo(BUTTON_SIZE)}// 第二个按钮secondButton.snp.makeConstraints { make inmake.leading.equalTo(firstButton.snp.trailing).offset(padding)make.centerY.equalToSuperview()make.size.equalTo(BUTTON_SIZE)}// 直播按钮startLiveButton.snp.makeConstraints { make inmake.centerX.equalToSuperview()make.bottom.equalTo(backView)make.size.equalTo(CGSize(width:  72.0, height: 72.0))}// 第三个按钮thirdButton.snp.makeConstraints { make inmake.trailing.equalTo(fourthButton.snp.leading).offset(-padding)make.centerY.equalToSuperview()make.size.equalTo(BUTTON_SIZE)}// 第四个按钮fourthButton.snp.makeConstraints { make inmake.trailing.equalToSuperview().offset(-padding)make.centerY.equalToSuperview()make.size.equalTo(BUTTON_SIZE)}}

可以像开播按钮一样,直接为按钮设置普通状态下的图片或者是选中状态下的图片,也可以采用配置的形式从配置信息中获取按钮的图片信息,这并不重要,接下来我们只需要把自定义的TabBar添加到自定义UITabBarController上即可,代码如下:

    func addTabbarView() {let tabbarView = CSTabbarView()tabbarView.frame = CGRect(x: 0.0, y: CS_SCREENHIGHT - cs_tabbarHeight, width: CS_SCREENWIDTH, height: cs_tabbarHeight)tabbarView.delegate = selfself.view.addSubview(tabbarView)self.tabbarView = tabbarView}

这里面的宽和高我们都可以设置为任意值,但是为了让它更贴近TabBar通常它的大小我们还是会设置的与系统UITabBar大小相同。

添加自定义动画

TabBar中的按钮原本的核心功能是用作切换UITabBarController中的子页面,但是如果可以添加一些流畅的动画无疑会提升一些用户体验,接下来我们就来实现它的点击事件并在此基础上添加一个脉冲式的动画。

首先我们需要为这些按钮添加点击事件,并用代理或者闭包的方式将点击事件传递到UITabBarController。

添加点击事件代码如下:

    func setEvent() {/// 第一个按钮firstButton.addTarget(self, action: #selector(buttonOnclick), for: .touchUpInside)/// 第二个按钮secondButton.addTarget(self, action: #selector(buttonOnclick), for: .touchUpInside)/// 第三个按钮thirdButton.addTarget(self, action: #selector(buttonOnclick), for: .touchUpInside)/// 第四个按钮fourthButton.addTarget(self, action: #selector(buttonOnclick), for: .touchUpInside)/// 开播按钮startLiveButton.addTarget(self, action: #selector(startLiveButtonTouch), for: .touchUpInside)}

事件实现代码如下:

    @objc func buttonOnclick(button:UIButton) {let index = button.tag - 100guard let selectedButton = selectedButton else { return }selectedButton.isSelected = falsebutton.isSelected = trueself.selectedButton = buttonguard let delegate = delegate else { return }delegate.tarBarItemTouch(index: index)addAnimaction(button: button)}// 开播@objc func startLiveButtonTouch() {addAnimaction(button: startLiveButton)delegate?.startLiveButtonTouch()}

可以看得出在这里我采用了代理的方式,将点击事件回调到UITabBarViewController,不过目前我们的关注重点应该是在动画上面,让我们来看一下动画实现,代码如下:

    // 添加动画func addAnimaction(button:UIButton) {let anim = CAKeyframeAnimation(keyPath: "transform.scale")anim.values = [1.0,1.1,0.9,1.0]anim.keyTimes = [0,0.2,0.8,1]anim.duration = 0.3button.layer.add(anim, forKey: "scale")}

一个非常简单的关键帧动画实现的脉冲效果,具体效果如下,(实际效果会比gif更流畅一些):

扩展功能

我们可以创建任何标签,不过最常见的还是带数量的小红点,由于它是完全自定义的,每一个按钮都是我们自己手动创建的,那么完全可以通过创建一个带标签的按钮来实现这个需求。

那么就继承自UIButton来创建一个带数量标签的按钮,具体代码如下:

class CSTabBageButton: UIButton {/// 角标背景var bageView = UIView()/// 角标var bageLabel = UILabel()/// 角标值var bageValue: Int = 0 {didSet {if bageValue <= 0 {bageView.isHidden = true} else {bageView.isHidden = falsebageLabel.text = "\(bageValue)"}}}override init(frame: CGRect) {super.init(frame: frame)setupView()setLayout()}required init?(coder: NSCoder) {fatalError("init(coder:) has not been implemented")}func setupView() {bageView.isHidden = trueself.addSubview(bageView)bageView.backgroundColor = .redbageView.layer.masksToBounds = truebageView.layer.cornerRadius = 7.5bageView.addSubview(bageLabel)bageLabel.textColor = .whitebageLabel.font = UIFont.systemFont(ofSize: 10)bageLabel.textAlignment = .center}func setLayout() {bageView.snp.makeConstraints { make inmake.leading.equalTo(self.snp.trailing).offset(-5.0)make.top.equalTo(self).offset(-5.0)make.height.equalTo(15.0)}bageLabel.snp.makeConstraints { make inmake.leading.equalToSuperview().offset(5.0)make.width.greaterThanOrEqualTo(5.0)make.centerY.equalToSuperview()make.trailing.equalToSuperview().offset(-5.0)}}}

然后我们用它来替换掉第三个按钮,假设我们已经收到消息,将按钮的标签数量设置为3,看一下效果,代码如下:

    /// 第三个按钮let thirdButton = CSTabBageButton()....thirdButton.bageValue = 3

效果如下:

丰富交互设计

相对于系统UITabBar较为单一的交互设计,自定义TabBar的交互非常灵活,比如说中间的开播按钮,我并没有给它绑定任何属于UITabBarViewController的子视图控制器,它的点击事件我可以用来处理任何事情。

下面我们先来看一下自定义TarBar点击事件的代理,以及代理事件的实现。

代理声明代码如下:

/// 点击代理
protocol CSTabbarTouchDelegate: AnyObject {/// tabbar按钮点击func tarBarItemTouch(index:Int)/// 开播按钮点击func startLiveButtonTouch()
}

没有提供默认的实现,那么就意味着它的遵循者必须要实现这些函数,CSCustomTabBarVC中的实现如下:

    //MARK: tabbar点击代理事件func tarBarItemTouch(index: Int) {var currentIndex = indexself.selectedIndex = currentIndex}/// 开播func startLiveButtonTouch() {// 检查 是否是游客登录if CSTouristHelper.shared.checkTouristLogin(loginSuccess: nil) {return}CSLiveShowManager.shared.showBroadcast()}

另外四个按钮我们并没有特殊干预,而是直接切换了对应的子视图控制。

而中间的开播按钮,我们自己不仅让它做了切换子控制器以外的操作,而且在操作前还进行了一些列的判断和其它操作。

可以看出我们的操作空间很大,不管是从设计,功能,还是交互,自定义UITabBar都非常灵活。

结语

通过本文的介绍和示例代码,我们探索了如何在iOS应用中自定义UITabBar,以解决系统UITabBar的各种痛点。我们不仅学会了如何改变外观、添加动画效果、还探讨了如何扩展功能和丰富交互设计。

通过继承UIView来创建自定义的TabBar,我们可以更加灵活地实现各种设计需求和交互效果,从而为用户提供更加个性化和优质的体验。希望这篇博客能为你提供有价值的指导和灵感,帮助你在iOS开发中更好地运用自定义UITabBar。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • org.springframework.context.ApplicationContext发送消息
  • filebeat把日志文件上传到Es中配置(ES7版本)
  • 喜讯丨泰迪智能科技实力中标湖北民族大学数学与统计学院一流专业实验室建设项目
  • js | Core
  • 代码随想录学习 day54 图论 Bellman_ford 算法精讲
  • Oracle线上执行SQL特别慢的原因分析
  • Spring Boot(八十一):Sa-Token快速实现API接口签名安全校验
  • 《JavaSE》---21.<简单认识Java的集合框架包装类泛型>
  • matlab simulink气隙局部放电仿真技术研究
  • 靖江美食元宇宙
  • openlayers 3d 地图 非三维 立体地图 行政区划裁剪 地图背景
  • 基于jeecgboot-vue3的Flowable流程仿钉钉流程设计器-支持VForm3表单的选择与支持
  • 安装好anaconda,打开jupyter notebook,新建 报500错
  • 二叉树的构造问题 | LeetCode刷题笔记 | 每日练习 | 深度优先遍历| 广度优先遍历 | Java
  • 【必看】基于LSTM网络的温度预测
  • 【技术性】Search知识
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • emacs初体验
  • golang中接口赋值与方法集
  • IOS评论框不贴底(ios12新bug)
  • Java 23种设计模式 之单例模式 7种实现方式
  • Java|序列化异常StreamCorruptedException的解决方法
  • Java小白进阶笔记(3)-初级面向对象
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • React+TypeScript入门
  • Service Worker
  • Terraform入门 - 1. 安装Terraform
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 从重复到重用
  • 复习Javascript专题(四):js中的深浅拷贝
  • 力扣(LeetCode)56
  • 批量截取pdf文件
  • 前嗅ForeSpider教程:创建模板
  • 算法-插入排序
  • 我是如何设计 Upload 上传组件的
  • 我有几个粽子,和一个故事
  • 写代码的正确姿势
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • ​用户画像从0到100的构建思路
  • ​油烟净化器电源安全,保障健康餐饮生活
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #include到底该写在哪
  • #window11设置系统变量#
  • (1)Android开发优化---------UI优化
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (杂交版)植物大战僵尸
  • (转)Oracle存储过程编写经验和优化措施
  • (转)一些感悟
  • (转载)CentOS查看系统信息|CentOS查看命令
  • .aanva
  • .cfg\.dat\.mak(持续补充)
  • .NET 4.0中的泛型协变和反变
  • .net 8 发布了,试下微软最近强推的MAUI
  • .NET COER+CONSUL微服务项目在CENTOS环境下的部署实践