来自Xib的自定义UIView – 要求,良好实践

我试图围绕使用Xib文件创建自定义 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的Owner设置为CustomView

现在,有趣的部分开始了。 如果您看一下在线可用的许多资源( 这里或这里 ),每个人都创建一个从两个初始化器调用的commonInit方法,我理解它是在我们的CustomView的两种初始化方式之间保持一致,但是有点神奇这就是为什么在这些方法中我们为我们的contentView加载Nib,对它加上约束并将它作为子视图添加到我们的类中? 只需查看代码注释:

 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) } 

它与更冗长的方法有何不同,它是否真的可以自动布局和一切正常工作?