确定 `UIView` 是从 XIB 加载还是从代码实例化

Posted

技术标签:

【中文标题】确定 `UIView` 是从 XIB 加载还是从代码实例化【英文标题】:Determine if `UIView` is loaded from XIB or instantiated from code 【发布时间】:2017-04-03 13:48:13 【问题描述】:

我使用以下代码强制UIView 的子类从名称为实际类名的 XIB 文件中加载:

class NibView : UIView 
    override func awakeAfter(using aDecoder: NSCoder) -> Any? 
        guard isRawView() else  return self 
        for view in self.subviews 
            view.removeFromSuperview()
        

        let view = instanceFromNib()
        return view
    

    func isRawView() -> Bool 
        // What here?
    

isRawView() 方法的目的是确定这个视图是从代码创建的,还是从相应的 XIB 文件加载的。 到目前为止我使用的实现是:

func isRawView() -> Bool 
    // A subview created by code (as opposed to  being deserialized from a nib)
    // has 2 subviews, both implementing the `UILayerSupport` protocol
    return
        self.subviews.count == 2 &&
        self.subviews.flatMap(
             $0.conforms(to: UILayoutSupport.self) ? $0 : nil ).count == 2

它使用一种技巧来确定视图是否是从代码创建的,因为在这种情况下它正好包含 2 个子视图,都实现了UILayoutSupport 协议。

NibView 子类从代码中实例化时,这很有效。但是如果视图是作为情节提要中视图控制器的一部分创建的,则它不起作用(并且可能同样适用于视图控制器和从 XIB 文件加载的视图)。

长篇大论来解释我的问题的原因:有没有办法让UIView 知道它是否是从 XIB 文件加载的,并且可能是该文件的名称? 或者,否则,实现isRawView() 方法的另一种方法应该是:

如果视图已从关联的 XIB 文件(其名称是类名)反序列化,则返回 false 否则返回true

【问题讨论】:

【参考方案1】:

利用提供的init 函数。

init(frame:​) -> 来自代码 init(coder:​) -> 来自笔尖

示例代码:

override init(frame: CGRect) 
    super.init(frame: frame)
    print("From code")


required public init?(coder aDecoder: NSCoder) 
    super.init(coder: aDecoder)
    print("From nib")

我可以这样打印出类名:

print(NSStringFromClass(type(of: self)).components(separatedBy: ".").last ?? "Couldn't get it")

你应该能够使用它,也许稍作调整,就可以得到你需要的东西。

【讨论】:

这可以帮助我区分代码和xib,但我认为它并不能告诉我使用了哪个xib。 @Antonio;我用一些可以打印类名的代码更新了我的答案。 这是 xib 文件名,而不是我要查找的类名。 super.init(coder: aDecoder) 内部调用awakeAfter(using:),在该方法中我需要确定当前视图是否已从名称为类名的 xib 文件中加载。 您的 xib 没有“链接”到它的自定义类吗?因为你可以有一个属性,即你在代码中设置的nibName,然后你可以检查view.nibName? 我可以,但我仍然需要确定何时可以设置它。如果awakeAfter(using:) 没有返回self,则意味着它创建并返回了一个新视图。为了设置该属性,我需要在新实例上调用 awakeAfter(using:) 之前执行此操作,但这会由 init?(coder:) 初始化程序自动调用,而这恰好为时已晚。

以上是关于确定 `UIView` 是从 XIB 加载还是从代码实例化的主要内容,如果未能解决你的问题,请参考以下文章

xib 指定的位置在 UIView 加载时移动?

UIView layoutSubviews 没有被调用

UIView:从自定义 XIB 加载导致崩溃

对于直接从 Xib 加载的 UIView,何时应用 UIView 大小类?

从 Textfield.inputview 加载 xib UIView

使用 XIB 加载的 UIView 按钮不起作用