美文网首页
Xib是如何加载显示出来的?

Xib是如何加载显示出来的?

作者: liaoworkinn | 来源:发表于2020-11-04 09:37 被阅读0次

    前一阵子在探究XCode12 + iOS14的UITableViewCell ContentView 显示层级bug时发现了一个问题:

    当CollectionViewCell 是由Xib加载的,就不会有问题。

    由此开始了对Xib是如何加载出来的探究。

    先说大家都知道的:

    (1).xib的本质是一个xml文件, 在xib文件上右键[Open as]->[Source Code]可以查看其xml源码。

    (2).xib创建View 走的是 init?(coder: NSCoder)方法,纯代码走的是init(frame: CGRect)

    那么标准库又是怎么通过init?(coder: NSCoder)把视图加载出来的呢?

    下面会从流程和解析两方面来讲Xib是如何加载显示的

    流程

    系统在编译的时候,会将xib文件编译生成.nib文件,这个在app的ipa文件里能找到。

    先用xib创建一个UIView 的子类NibView
    我们在项目里加载一个xib的写法:

     let nibView = Bundle.main.loadNibNamed("NibView", owner: self, options: nil)?.first as! NibView
     view.addSubview(nibView)
    

    当执行代码loadNibNamed的时候只做了三件事:

    1. 根据你的NibName找到nib文件, 再通过UINib(dataforpath: nibPath) 方法生成data数据。

       // 获取Bundle path
       let nibPath = Bundle.main.path(forResource: "NibView", ofType: "nib")
       // 私有api 通过path 生成data数据
       let nibData = UINib(dataforpath: nibPath)
      
    2. UINibDecoder(系统私有类) 会根据data数据创建自己的UINibDecoder实例,
      这里的UINibDecoder实例也就是init?(coder: NSCoder)传入的coder。

       // UINibDecoder(私有类)通过data创建实例
       let coder = UINibDecoder(readingData:nibData, error: error)
      
    3. 最后通过第二步获得的coder,调用对应NibView的init?(coder: NSCoder) 方法创建视图。

       let nibView = NibView(coder: coder)
      

    至此一个xib生成的View就创建了出来。

    那么我还有一个疑问?

    UIView 又是如何通过 init?(coder: NSCoder) 去创建具体的视图的呢?

    我们往之前创建的NibView的UIView的子类 往上面添加一个NibButton,如图:


    image.png

    查看其xml文件,其中红色部分是当前View的信息,黄色部分是subviews的标签,不用细看,下面会讲 :

    image.png

    当调用 init?(coder: NSCoder)时, 通过coder的decodeObject(forKey: String)方法。获取对应的UI信息。 这里的key是系统规定好的一些key。通过尝试发现了下面一些有用的key。

    class NibView: UIView {
    
        required init?(coder: NSCoder) {
            super.init(coder: coder)
            /// 这里的coder为UINibDecoder 类 isSameClass为 true
            let isSameClass = coder.isKind(of: NSClassFromString("UINibDecoder")!)
    
            // coder 通过"UISubviews" key 获取到子视图的信息,在cocoa系统中key为"NSSubviews"
            let nibSubviews = coder.decodeObject(forKey: "UISubviews")!
            print(nibSubviews)
        }
    }
    

    此时控制台打印信息为


    image.png

    如果在xml文件的subviews标签下修改 button 的属性,这里打印出的信息也会跟着改变。

    那么是不是subviews标签的内容就对应着之前coder的key "UISubviews"

    这里是不一定。在一些UIView的子类如会有一些其他处理,如tableviewCell。

    像tableviewCell会外部自动包裹一层contentView,contentView的中coder的key "UISubviews"所对应的内容才是subviews标签下的内容。

    tableviewCell的xib文件, 其他都不用看,知道几个框框的对应的标签就行:


    image.png

    不过可以肯定的是都是通过coder 都是通过 读取 key"UISubviews"的值来获取子视图。
    然后添加到视图上。

    到这里,xib是如何加载并显示到view上整个过程已经明了了。

    还有像下面一些有趣key可以解析到对应的UI属性信息,在cocoa中将UI前缀改成NS前缀即可。

        coder.decodeBool(forKey: "UIOpaque")
        coder.decodeBool(forKey: "UIHidden")
        coder.decodeObject(forKey: "UIBackgroundColor")
    

    感兴趣的同学可以在项目中print试试。

    相关文章

      网友评论

          本文标题:Xib是如何加载显示出来的?

          本文链接:https://www.haomeiwen.com/subject/tqcqvktx.html