【发布时间】:2018-09-03 22:43:51
【问题描述】:
我正试图围绕使用 Xib 文件创建 custom UIView 的主题。我已经做过很多次了,但我从来没有想过为什么这个过程如此复杂,以及哪些部分是必要的,哪些是好的等等。而现在,因为 Xibs 是我正在从事的项目的主要组成部分,我开始质疑一切 - 我需要确定发生了什么????
假设我们刚刚创建了一个简单的UIView 子类——我们称之为CustomView,这个类的基本要求是实现所需的初始化器,如下所示:
class CustomView: UIView {
// This one is for initializing programmatically
override init(frame: CGRect) {
super.init(frame: frame)
}
// This one is for Storyboards and Xibs
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
当然我们已经关联了 Xib 文件,所以我们实际上要做的第一件事就是去那里并将 File's Owner 设置为 CustomView。
现在,有趣的部分开始了。如果您查看许多在线可用资源(here 或 here),每个人都会创建一个 commonInit 方法,该方法从两个初始化程序中调用,我知道这是为了在我们的两种初始化方式之间保持一致CustomView,但有点神奇的是为什么在这些方法中我们为 contentView 加载 Nib,对其施加约束并将其作为子视图添加到我们的类中?
看代码cmets就行了:
class CustomView: UIView {
@IBOutlet weak var contentView: UIView?
@IBOutlet weak var someSubview: UIView?
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
// Why we're loading this `contentView` from Xib?`
self.contentView = loadViewFromNib()
// Why do we have to add it when I's already added in IB?
self.addSubview(contentView)
// Why do we need a constraints for it when IB shows it's filling whole view?
self.contentView.translatesAutoresizingMaskIntoConstraints = false
let trailingAnchor = contentView.trailingAnchor.constraint(equalTo: trailingAnchor)
let leadingAnchor = contentView.leadingAnchor.constraint(equalTo: leadingAnchor)
let topAnchor = contentView.topAnchor.constraint(equalTo: topAnchor)
let bottomAnchor = contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
NSLayoutConstraint.activate([trailingAnchor, leadingAnchor, topAnchor, bottomAnchor])
}
private func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
let nibView = nib.instantiateWithOwner(self, options: nil).first as! UIView
return nibView
}
}
以下是令人烦恼的问题:
- 首先为什么我们必须在我们的子类中添加
contentView? - 为什么我们要从 Xib 文件中加载这个
contentView,而它已经是@IBOutlet?不应该免费给我们吗?我想这是为了使用init(frame:)从代码中初始化我们的视图,对吧? - 为什么我们需要将这个
contentView添加为子视图并设置它的约束,而 Xib 已经指定了所有内容?
我还看到了上述代码的 更短 版本,没有约束,并且从 Nib 的层次结构中完整加载了第一个 UIView:
private func commonInit() {
Bundle.main.loadNibNamed(String(describing: CustomView.self), owner: self, options: nil)
guard let contentView = contentView else { return }
self.addSubview(contentView)
}
它与 更详细 方法有什么不同,它在自动布局和所有东西上都能正常工作吗?
【问题讨论】: