最近开始对项目进行代码重构, 其中一项工作就是将部分UI组件抽离出来. 我们对UI基本上都是用XIB或者StoryBoard写的, 因此抽离出来的组件也使用XIB来构建.
XIB组件可以有两种方式实现, 区别主要在把代码文件给File's Owner还是自定义View的Class.
如果使用自定义View的Class, 那么你无需在代码文件中写太多其他逻辑, 可以使用Bundle API初始化自定义View, 逻辑代码会在awakeFromNib方法中执行.
let view = Bundle.main.loadNibNamed("XibView", owner: self, options: nil)?.first as! XibView
view.addSubview(view)
//当然你也可以把这个初始化方法写在自定义View的类里, 给他起个名字
复制代码
这个初始化方法有一个问题, 就是无法在XIB或者StoryBoard上直接使用. 在StoryBoard中拖出一个UIView控件后设定其Class为自定义View类后, 它不会直接去找自定义自定义View的XIB文件, 而是去找自定义View类的初始化方法
init?(coder aDecoder: NSCoder)
复制代码
此时就会陷入一个死循环(比较复杂, 你们可以自己思考一下)
如果使用File's Owner, 那么需要重写init方法, 同样是使用Bundle API初始化自定义View, 并添加到自己的View上(因为此时的File's Owner本身也是UIView, 因此实际显示在屏幕上的View会有两层, 逼死强迫症患者).
一般的初始化方法
let view = Bundle.main.loadNibNamed("ArrowAnimateView", owner: self, options: nil)?.first as! UIView
override init(frame: CGRect) {
super.init(frame: frame)
view.frame = bounds
addSubview(view)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addSubview(view)
view.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
复制代码
运行一下, 一切似乎都很正常. 然后我尝试将自定义View中的控件链接到代码中.
@IBOutlet weak var topImg: UIImageView!
let view = Bundle.main.loadNibNamed("ArrowAnimateView", owner: self, options: nil)?.first as! UIView
override init(frame: CGRect) {
super.init(frame: frame)
view.frame = bounds
addSubview(view)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addSubview(view)
view.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
复制代码
紧接着就是一个崩溃: this class is not key value coding-compliant for the key type topImg. 这个崩溃告诉我们XIB中的链接属性出现了错误, 但是我仔细查看了一下, 并没有发现问题. 在思考了许久后, 我将问题定位到了初始化时机上.
因为通过Bundle加载的这个View是全局属性, 因此我很自然的使用了
let view = Bundle.main.loadNibNamed("ArrowAnimateView", owner: self, options: nil)?.first as! UIView
复制代码
这个方法来初始化它, 很简洁. 但是它却犯了一个天大的错误. 这种全局属性是在类load的时候创建的, 此时的类还没有走init方法, 因此此时的类(或者说File's Owner)还没有初始化好. 而我们的XIB需要File's Owner来确定控件链接, 所以当他尝试初始化的时候发现File's Owner(自定义View类)并不存在, 这就造成了崩溃.
解决办法其实很简单
lazy var view: UIView = {
return Bundle.main.loadNibNamed("ArrowAnimateView", owner: self, options: nil)?.first as! UIView
}()
复制代码
使用懒加载, 这样XIB的初始化只有在addSubview(view)时才会触发, 而此时类肯定已经初始化完毕了.
@IBOutlet weak var topImg: UIImageView!
lazy var view: UIView = {
return Bundle.main.loadNibNamed("ArrowAnimateView", owner: self, options: nil)?.first as! UIView
}()
override init(frame: CGRect) {
super.init(frame: frame)
view.frame = bounds
addSubview(view)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addSubview(view)
view.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
}
复制代码
最终代码