利用kvc对UITabBar上的UITabBarButton的尝试修改.md
一、前言
一次比较懒的想法,不想自定义UITabBar,也不想用第三方框架,于是想尝试修改苹果私有类来达到部分效果
效果如下
- 点击tabBar 上的按钮,图片有变大再变小的动画
- tabBar 上某个按钮(购物车按钮),点击弹出控制器是modal出来的,并不属于UITabBarController管理的
- 上述购物车按钮,是后面加到tabBar 上的自定义的按钮UIButton,本身没有右上角的badge的提示,懒得话,就用
系统UITabBarButton 的badge - 点击商品的加号按钮,实现tabBar上购物车按钮 显示的badge的数字的改变,并实现变大变小的动画

二、相关说明
在swift3及xcode8 beta2 环境下
运行时遍历出成员变量
var count:UInt32 = 0
let ivarlist = class_copyIvarList(NSClassFromString("UITabBarButton")!, &count)
for index in 0..<numericCast(count) {
let ivar = ivarlist![index]
let ivarStr = String.init(utf8String: ivar_getName(ivar!))
print("\(ivarStr)")
}
- 1、UITabBarButton
Optional("_hitRect")
Optional("_info")
Optional("_vibrancyEffectView")
Optional("_label")
Optional("_badge")
Optional("_selectedIndicator")
Optional("_selected")
Optional("_infoInsets")
Optional("_selectedInfoOffset")
Optional("_infoOffset")
Optional("_customSelectedIndicatorImage")
Optional("_labelOffset")
Optional("_buttonTintColorsForState")
Optional("_contentTintColorsForState")
Optional("_badgeColor")
Optional("_badgeTextAttributesForState")
Optional("_showsHighlightedState")
Optional("_centerAllContents")
Optional("_appearanceGuideClass")
Optional("_tabBar")
其中 "_info" ,"_label" ,"_badge" 这三个是我们需要的
- "_info"是 UIImageView 就是显示的图片,"_label"是UILabel 显示的文字
2、"_badge"是什么呢?它真实类型是什么,子控件及属性名是什么
let tab = window?.rootViewController as! TabBarViewController
for view in tab.tabBar.subviews {
if view.isKind(of: NSClassFromString("UITabBarButton")! ) {
let badgeView = view.value(forKeyPath: "_badge")
print("1\(badgeView!.self)")
let badge = badgeView as! UIView
print("2\(badge.subviews)")
}
}
}
1 <_UIBadgeView: 0x7fd671c336c0; frame = (33 2; 18 18); text = '1'; userInteractionEnabled = NO; layer = <CALayer: 0x608000031240>>
2 [<UIImageView: 0x7fd671c09570; frame = (0 0; 18 18); opaque = NO; userInteractionEnabled = NO; tintColor = UIExtendedSRGBColorSpace 1 0.231373 0.188235 1; layer = <CALayer: 0x60800002e9a0>>, <UILabel: 0x7fd671c33ba0; frame = (6 1; 6 16); text = '1'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60800009c890>>]
var count:UInt32 = 0
let ivarlist = class_copyIvarList(NSClassFromString("_UIBadgeView")!, &count)
for index in 0..<numericCast(count) {
let ivar = ivarlist![index]
let ivarStr = String.init(utf8String: ivar_getName(ivar!))
print("badge= \(ivarStr)")
}
badge= Optional("_label")
badge= Optional("_background")
badge= Optional("_mergedTextAttributes")
badge= Optional("_text")
badge= Optional("_textAttributes")
badge= Optional("_backgroundColor")
三、做法及代码
自定义TabBarController里,添加4个子控制器,其中第三个添加一个空的UIViewController,图片为UIImage(), 空的TabBarItem,只是让这个系统生成的UITabBarButton占好位置
自定义UIButton,添加到TabBar上,盖住上述 空的UITabBarButton上
appDelegate成为TabBarController的代理,监控tabBar的点击
extension AppDelegate: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
for view in tabBarController.tabBar.subviews {
if view.isKind(of: NSClassFromString("UITabBarButton")! ) {
// UITabBarButton是继承UIControl的, 通过 按钮的状态 才判别是否是现在点击的按钮
let state = view.value(forKeyPath: "highlighted")
let stateValue = state as! Int
if stateValue == 1 {
// 拿到当前点击的 按钮的imageView
let imageView = view.value(forKeyPath: "_info")
guard let temp = imageView else {
return
}
let tabButtonImgView = temp as! UIImageView
// 添加动画
UIView.animate(withDuration: 0.2, animations: {
tabButtonImgView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
}, completion: { (Bool) in
tabButtonImgView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
})
}
}
}
}
}
由于UITabBarButton的子控件都是懒加载的,需要用控制器的tabBarItem 模型对UITabBarButton进行设置
- 点击商品加号按钮,拿到 控制器tabBarItem,设置badgeValue ,然而购物车按钮(自己添加的) 盖住了部分 系统生成的badgeView,需要调整badgeView的位置
for button in tabBar.subviews {
if button.isKind(of: NSClassFromString("UITabBarButton")! ) {
let label = button.value(forKeyPath: "_label") as! UILabel
// print("\(label.text)")
if label.text == "" { // 通过label.text 确定是购物车按钮 那位置的 UITabBarButton
let badgeButton = button.value(forKeyPath: "_badge")
// print("\(badgeButton!.self)")
guard let badgeView = badgeButton else {
return
}
let badge = badgeView as! UIView
// print("\(badge.subviews)")
badge.frame.origin.x = 52 // 可根据需要计算
}
}
}
- 点击商品加号按钮实现系统 badgeView的动画
let tab = window?.rootViewController as! TabBarViewController
for button in tab.tabBar.subviews {
if button.isKind(of: NSClassFromString("UITabBarButton")! ) {
let label = button.value(forKeyPath: "_label") as! UILabel
// 外界通过 lable.text 区别出是我们需要的 第三个UITabBarButton
if label.text == "" {
let badgeButton = button.value(forKeyPath: "_badge")
guard let badgeView = badgeButton else {
return
}
// 拿到 badge 控件
let badge = badgeView as! UIView
// num变量为 点击加号按钮, 商品个数 计量
let str:NSString = NSString.init(format: "%d", num)
// kvc 设置属性
badgeView.setValue(UIFont.systemFont(ofSize: 9), forKeyPath: "_label.font")
badgeView.setValue(str, forKeyPath: "_label.text")
let tabLabal = badgeView.value(forKeyPath: "_label")
let label = tabLabal as! UILabel
// 拿到lebel 计算label 的大小、尺寸
let size = str.size(attributes: [NSFontAttributeName : UIFont.systemFont(ofSize: 9)])
label.frame = CGRect(x: (badge.frame.size.width - size.width) * 0.5, y: (badge.frame.size.height - size.height) * 0.5, width: size.width, height: size.height)
// 添加核心动画
let scaleAni = CABasicAnimation()
scaleAni.keyPath = "transform.scale"
scaleAni.fromValue = 1.0
scaleAni.toValue = 1.2
scaleAni.autoreverses = true
scaleAni.duration = 0.25
badge.layer.add(scaleAni, forKey: nil)
}
}
}
四、 问题
- 刚学习swift 3.0, 各种不熟,各种不顺,加上xcode8 beta版各种不稳定及bug,这次仅仅是尝试swift3的改变而练习的
- 系统UITabBarButton 里面的子控件都是懒加载的,没有先设置tabBarItem模型 ,用kvc 是赋不上值得,这点得注意
五、参考的 小demo
利用kvc对UITabBar上的UITabBarButton的尝试修改.md的更多相关文章
- Java 利用SWFUpload多文件上传 session 为空失效,不能验证的问题 swfUpload多文件上传
Java 利用SWFUpload多文件上传 session 为空失效,不能验证的问题(转) 我们都知道普通的文件上传是通过表单进行文件上传的,还不能达到异步上传的目的.通过使用某些技术手段,比如jqu ...
- selenium+java利用AutoIT实现文件上传
转自https://www.cnblogs.com/yunman/p/7112882.html?utm_source=itdadao&utm_medium=referral 1.AutoIT介 ...
- 利用SQLite在android上实现增删改查
利用SQLite在android上实现增删改查 方法: 一.直接利用database.execSQL()方法输入完整sql语句进行操作 这种方法适用于复杂的sql语句,比如多表查询等等 这里适合于增删 ...
- 利用SQLite在android上创建数据库
利用SQLite在android上创建数据库 方法: 1.创建我们的数据库类继承SQLiteOpenHelper类 完成相关函数的重写和数据库对象的初始化 public MySQLiteOpenHel ...
- 利用Tengine在树莓派上跑深度学习网络
树莓派是国内比较流行的一款卡片式计算机,但是受限于其硬件配置,用树莓派玩深度学习似乎有些艰难.最近OPENAI为嵌入式设备推出了一款AI框架Tengine,其对于配置的要求相比传统框架降低了很多,我尝 ...
- [转]利用 Commons-Fileupload 实现文件上传
转载 Java Web开发人员可以使用Apache文件上传组件来接收浏览器上传的文件,该组件由多个类共同组成,但是,对于使用该组件来编写文件上传功能的Java Web开发人员来说,只需要了解和使用其中 ...
- 利用TortoiseGit向Github上传文件
利用TortoiseGit向Github上传文件 第一步:建一个新文件夹,作为本地仓库 第二步:右键选择设置为版本库 若弹出,确认即可 重新打开改文件,会发现多了一个绿色的小勾 在文件夹中会自动生成一 ...
- php文件上传大小限制的修改方法大全
php文件上传大小限制的修改方法大全 基本就是修改maxsize选项,当然为了提高上传文件的成功率,还需要设置超时时间等. 文章如下: [php文件上传]php文件上传大小限制修改,phpmyadmi ...
- 【转】利用 three.js 开发微信小游戏的尝试
前言 这是一次利用 three.js 开发微信小游戏的尝试,并不能算作是教程,只能算是一篇笔记吧. 微信 WeChat 6.6.1 开始引入了微信小游戏,初期上线了一批质量相当不错的小游戏.我在查阅各 ...
随机推荐
- SMARTFORM报表程序设计(1)
SMARTFORM是SAP提供的一款商务单据及报表设置工具,可以在FORM中实现数据的计算及转换等功能,并能在FORM创建的同时生成功能模块,为FORM和ABAP程序提供更为强大的参数接口.输入T-C ...
- Class hierarchy of UIResponder as well as subclasses of UIView and UIControl
When you were dragging in your label and your button to this view, you were adding them as subviews. ...
- [转载]GMT地形数据总结
[转载]GMT地形数据总结 原文地址:GMT地形数据总结作者:Jason 转载:http://seisman.info/gmt-topo-grid-datas.html 目前接触到的地形数 ...
- asp.net负载均衡方案[转]
在前面的几篇文章中,主要谈到了在Discuz!NT中的跨站缓存数据,数据库负载均衡.但如果要实现将产品分布式布置到若干机器,组成集群来共同支撑起整个业务的话,还是有一定问题的(后面会有所介绍).下面先 ...
- TP复习16
'APP_GROUP_LIST' => 'Home,Admin', //项目分组设定'DEFAULT_GROUP' => 'Home', //默认分组 开分组复制上面 AjaxReturn
- TP复习10
i * { padding:0; margin:0; } 居中 ## ThinkPHP 3.1.2 模板中的变量#讲师:赵桐正微博:http://weibo.com/zhaotongzheng 本节课 ...
- NSNotificationCenter通知中心
概述 NSNotificationCenter通知中心,通常用于一对一或者一对多的消息传递,即当一个地方改变时,要求改变其他的一些地方,例如当网络请求回来了新的数据,需要刷新本地信息和本地内存里面的界 ...
- innodb_lru_scan_depth
innodb_lru_scan_depth是5.6新增加的参数,根据 官方文档 描述,它会影响page cleaner线程每次刷脏页的数量, 这是一个每1秒 loop一次的线程.在Innodb内部, ...
- Trace、Debug和TraceSource的使用以及日志设计 .
[-] Trace 和 Debug区别 什么是Listeners 跟踪开关 使用BooleanSwitch开关 使用TraceSwitch开关 使用TraceSource代替Trace和Debug 设 ...
- 关于c中的%x及其它格式化符
原文:http://blog.csdn.net/lincyang/article/details/6252443 格式化: %x表示按16进制输出:int a = 16;%02x:输出10:%03x: ...