Animated-tab-bar源码分析
animated-tab-bar
Swift
基础
注释:// MARK: - <#Description#>
里面用到了比较多的set
,get
,didSet
、didGet
。类似于OC中重写get、set
方法。在改变了某些数值值得时候,便于改变UI显示的效果。如:
open override var isEnabled: Bool {
didSet {
iconView?.icon.alpha = isEnabled == true ? 1 : 0.5
iconView?.textLabel.alpha = isEnabled == true ? 1 : 0.5
}
}
Swift 3 新关键字
Swift 3必看:新的访问控制fileprivate和open
fileprivate
在swift 3中,新增加了一个 fileprivate来显式的表明,这个元素的访问权限为文件内私有。过去的private对应现在的fileprivate。现在的private则是真正的私有,离开了这个类或者结构体的作用域外面就无法访问。
open
通过open和public标记区别一个元素在其他module中是只能被访问还是可以被override。
现在的访问权限则依次为:open,public,internal,fileprivate,private。
有的人会觉得访问权限选择的增加加大了语言的复杂度。但是如果我们思考swift语言的设计目标之一就是一门安全的语言(“Designed for Safety”)就能理解这次的改动。更加明确清晰的访问权限控制可以使程序员表达出更准确的意图,当然也迫使在编码时思考的更加深入。
// 这个属性在ModuleA的范围外不能被override
public var size : Int
// 这个方法在ModuleA的范围外不能被override
public func foo() {}
// 这个方法在任何地方都可以被override
open func bar() {}
注意:无论在任何地方,默认的访问关键字都是internael。模块内访问
Scoped Access Level
代码分析
整个项目的目录结构
接下来就一个一个的分析其中的带,以及他们的关系。
RAMAnimatedTabBarController
RAMAnimatedTabBarItem
自定义的tabBarItem,继承系统UITabBarItem。为了达到扩展,设计的时候把一些属性设计为子类列以继承的。比如:
// The current badge value
open var badge: RAMBadge? // use badgeValue to show badge
// Container for icon and text in UITableItem.
// 元组把信息包装在一起
open var iconView: (icon: UIImageView, textLabel: UILabel)?
顺便提一下元组使用的方式。一般元组用于传递包装参数。比如iconView
,把icon,和textLabel包装在一起了。
做动画调用者把相关的传递过去
/**
Start selected animation
*/
open func playAnimation() {
assert(animation != nil, "add animation in UITabBarItem")
guard animation != nil && iconView != nil else {
return
}
animation.playAnimation(iconView!.icon, textLabel: iconView!.textLabel)
}
animation.playAnimation(iconView!.icon, textLabel: iconView!.textLabel)
解析出元组里面的数据
做动画相关的方法playAnimation()
,deselectAnimation()
,selectedState()
在做动画的时候需要对各个参数进行判断,如上所示。
通过扩展为类动态添加属性(可以类比OC中如何实现)
这点类似于系统中以分类的形式把功能组织在分类中。比如:
extension RAMAnimatedTabBarItem {
/// The current badge value
override open var badgeValue: String? {
get {
return badge?.text
}
set(newValue) {
if newValue == nil {
badge?.removeFromSuperview()
badge = nil;
return
}
if let iconView = iconView, let contanerView = iconView.icon.superview , badge == nil {
badge = RAMBadge.badge()
badge!.addBadgeOnView(contanerView)
}
badge?.text = newValue
}
}
}
RAMAnimatedTabBarController
扩展:在扩展方法里面实现了
changeSelectedColor
,animationTabBarHidden
,setSelectIndex
-
类型装换:
let items = tabBar.items as! [RAMAnimatedTabBarItem]
- 类似于OC中的
(转换类型)原始类型
。注意关键字as!
。然后再遍历。
- 类似于OC中的
-
对可变类型的判断:为了保证参数是合法的,所以必须对参数进行检验。这也是开发中经常需要做的。比如:
guard let items = tabBar.items as? [RAMAnimatedTabBarItem] else { fatalError("items must inherit RAMAnimatedTabBarItem") }
这样才保证参数合法。下面代码可以直接使用
items
进行操作
RAMAnimatedTabBarController
继承自系统UITabBarController
。首先是fileprivate
和private
的区别。
-
fileprivate
:保证在类所在的文件可以访问到这个变量。比如常见的写在一个文件中的分类。 -
private
:表示绝对的私有,只要出了这个类,则不可以访问到属性。
继承了系统了,就难免重写初始化方法。比如:
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.didInit = true
self.initializeContainers()
}
public init(viewControllers: [UIViewController]) {
super.init(nibName: nil, bundle: nil)
self.didInit = true
// Set initial items
self.setViewControllers(viewControllers, animated: false)
self.initializeContainers()
}
说道这里就需要弄清楚几个初始化方法的加载顺序及场景。网上找了几篇觉得如下几篇不错
loadView/viewDidLoad/initWithNibName/awakeFromNib/initWithCoder的用法总结
继续分析
在每一个初始化方法中都会调用一个initializeContainers
。为了确保在调用这个方法的时候师徒已经加载所以用了两个变量来保持当前视图的状态。
let containers = self.createViewContainers()
self.createCustomIcons(containers)
- 创建容器包括创建容器视图,及布局容器。代码是用添加约束的方式布局,而且使用格式视图标记语言。平时开发中用一般用
mansory
,在做第三方的时候回涉及到这种用法。通过用index
来区分各个容器。将各个容器放到一个字典里面。然后用字符串拼接出formatString - 给容器添加子视图。
fileprivate func createCustomIcons(_ containers : NSDictionary)
:先从数组和字典中取出每个item里面的容器及icon及lable。设置属性之后,添加约束。处理完之后最后由于是从系统中继承的,还需要把系统相关的属性清空。如:item.image = nil
- 点击item的逻辑.
- 参数检验
- 取出对应的控制器
- 判断一下当前的索引和选中的索引是否一样。不一样则做动画变化
- 分别对被选中、被释放的item做动画
- 做完动画刷新index.
- 告诉代理,那个控制器被选中。
delegate?.tabBarController?(self, didSelect: controller)