XIB自定义View的坑

最近开始对项目进行代码重构, 其中一项工作就是将部分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()
        }
    }
复制代码

最终代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值