确定 `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()
方法的另一种方法应该是:
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 大小类?