学习笔记 - 分析BRYXBanner的实现
今天微博上看到一款非常棒的可以带图片显示的下拉通知条及示例 - BRYXBanner,封装良好,调用方便。在此之前自己封装比这个简单多的Loading通知View,但每次调用非常麻烦,代码风格也不是很好。看了源码整个Class就200多行代码,而且使用swift编写。所以决定挨个分析其函数,语法及风格来学习类此模块的封装。
声明枚举
1 | private enum BannerState { |
第一个枚举BannerState的声明没什么好说的,最基本的枚举声明方式 - 枚举语法。声明了Banner的四中状态。
第二个枚举声明有点意思,使用了类型属性语法方式,将damping和velocity的组合springValues与BannerSpringiness的三中状态绑定。方便调用和统一修改。其引用方法为
1 | var a = BannerSpringiness.None |
声明Banner类
1 | /// Banner is a dropdown notification view that presents above the main view controller, but below the status bar. |
声明Banner为UIView,也没有什么特殊的地方,后续内容均为Banner类内的方法和变量。
待解决
1 | private class func topWindow() -> UIWindow? { |
声明私有变量
1 | private let contentView = UIView() |
前面是一些基本的私有变量和公共变量的声明,最后两个textColor
及hasShadows
的声明包含了一个didSet
属性观察器。
属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。
虽然这个概念很早就了解到了,但还真的一直未用过。之前相同场景我都是先赋值,然后再引用相关函数。原来用didSet
可以很方便的更新标题,配色等属性。
相关案例
1 | class StepCounter { |
属性计算
1 | /// The color of the background view. Defaults to `nil`. |
除存储属性外,类、结构体和枚举可以定义计算属性。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter,来间接获取和设置其他属性或变量的值。
如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称newValue。
只有 getter 没有 setter 的计算属性就是只读计算属性。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。只读计算属性的声明可以去掉get关键字和花括号:
getter和setter例子
1 | var x:Int! |
声明闭包
1 | /// A block to call when the uer taps on the banner. |
前两个为处理点击事件和消失后引用的闭包。没什么特殊的。更多相关知识 - 闭包
初始化界面元素
1 | /// The label that displays the banner's title. |
看这些代码真是涨姿势,原来还可以用闭包来创建并初始化界面元素。这个代码整体风格非常棒,结构简介明了。以后我得认真模仿这种代码风格。
初始化 - init
1 | /// A Banner with the provided `title`, `subtitle`, and optional `image`, ready to be presented with `show()`. |
这个初始化及备注说明写的非常优雅,title
, subtitle
, image
这三个可以根据需要设为nil
。初始化过程中除了基本的几个变量赋值外,其余的均函数调用。现在挨个分析这些函数。
super.init(frame: CGRectZero)
疑问?
重置(设置)阴影 - resetShadows()
1 | private func resetShadows() { |
这个方法在初始化时候及hasShadows
值变化时候被调用,主要用来设置阴影。其中需要关注的是这一行
1 | layer.shadowOpacity = self.hasShadows ? 0.5 : 0.0 |
这是一种判断条件的缩写方式,我也最近才学到。用法如下
1 | var condition = true //判断条件 |
添加手势动作 - addGestureRecognizers()
1 | private func addGestureRecognizers() { |
这里增加了两个手势动作,分别是点击事件didTap
和往上滑动事件didSwipe
。
子视图初始化 - initializeSubviews()
下面这个是大头,采取在代码上备注的方式记录
1 | private func initializeSubviews() { |
刚开始看着一段感觉好长一坨,肯定有很多很神奇的东西。认真看了一遍发现基本都是View的约束,只是Autolayout用起来比较操蛋而已。充分体现了Masonry等第三方Autolayout扩展的重要性。
重写didMoveToSuperview方法
1 | private var showingConstraint: NSLayoutConstraint? |
上述函数就是定义了三个NSLayoutConstraint,作为内部属性。根据显示状态改变。
显示函数 - show()
1 | public func show(duration: NSTimeInterval? = nil) { |
Banner的显示函数,可选参数duration
表示显示时间。若duration
未设置,则需要手动隐藏Banner。
其中要点之一,通过let window = Banner.topWindow()
获取当前需要显示的window,其中具体的实现topWindow()
不是很明白,周一请教一下同事。
将Banner添加(addSubview)到目标视图后,调用forceUpdates()
函数设置约束。
其中UIViewAnimation巧妙的利用最初声明的枚举来取适当的值,很好的做法。需要参考。
如果设置了duration
则利用延时函数dispatch_after
来设定消失时间,病调用dismiss
方法。
消失函数 - dismiss()
1 | public func dismiss() { |
消失函数跟显示函数一样,实质上只负责更新bannerState
这个枚举变量的值,当枚举变量bannerState
值发生变化时,通过属性观察器didSet
来调用界面更新函数forceUpdates()
。很方便且直观的做法,今后采取。
界面更新函数 - forceUpdates()
1 | private func forceUpdates() { |
这个函数可取之处也不少。
if let
函数可以带多个参数,同时满足所有条件情况下才运行。- 显示隐藏等动作,可以把具体的约束赋值给变量,然后通过
addConstraint
和removeConstraint
方式更新约束。
目前还有几点小疑问没能解决。后天请教同事后补充上。分析好的代码真是的好的学习方法。其中很多做法从来没想到过。真实涨姿势,以后有空就多看看别人怎么实现具体的功能的~