【问题标题】:Custom UIView from Xib - requirements, good practices来自 Xib 的自定义 UIView - 要求,良好实践
【发布时间】: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

现在,有趣的部分开始了。如果您查看许多在线可用资源(herehere),每个人都会创建一个 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)
}

它与 更详细 方法有什么不同,它在自动布局和所有东西上都能正常工作吗?

【问题讨论】:

    标签: ios swift uiview xib


    【解决方案1】:

    从 Nib 加载自定义视图有两种方法。您可以在 IB 中添加一个普通的 UIView 并将此视图设置为自定义类(类似于 UITableView 子类的设置方式)。注意在这种情况下不会修改文件所有者。当您从代码中加载 nib 时,您会直接获得一个完全正常工作的 UIView 子类。您有责任约束它并将其添加到视图层次结构中。您无法从 IB 本身添加此视图,因为您无法从 IB 加载它。

    第二个是将XIBs File Owner设置为自定义类。这就是在 Storyboards 之前设置 UIViewControllers 的方式。文件所有者是一个代理对象,加载时 IBOutlets 将连接到该代理对象。 这支持以编程方式和从 IB 本身构建,您只需在任何 Storyboard 中放置一个自定义 UIView 并将其自定义类设置为您的 UIView 子类,您在自定义子类中添加的代码将加载 XIB 并添加其内容视图。

    直接回答你的问题:

    • 实现自定义视图已经从调用代码创建(使用代码中的 MyCustomView() 或 IB 中的 UIView 对象)。因此,为了保持其身份和约束,您能做的最好的事情就是向其添加内容视图。
    • @IBOutlet 只是一个占位符,表示将为此对象设置出口。实际上加载 XIB 必须由您的代码完成(这由 UIViewController 在加载 Storyboard 或在 XIB 中使用 init(nibName:bundle:) 构造函数时为您完成)
    • XIB 包含内容视图的所有内容,而不是自定义视图。在这种方法中,XIB 中的 View 是一个普通的 UIView,并且必须正确地约束到它的父级(自定义视图)
    • 您提到的较短版本利用translatesAutoresizingMaskIntoConstraints 来创建基于等于父视图的框架的约束。

    编辑:self.contentView = loadViewFromNib() 行无关紧要。你不需要分配给contentView(或者从loadViewFromNib方法返回任何东西。你只需要在XIB文件中建立连接,它会在加载时自动设置。owner: self参数表示你的自定义类将负责处理所有连接

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-17
      • 2016-01-27
      • 1970-01-01
      • 2011-10-31
      • 2016-05-18
      • 2018-12-02
      • 1970-01-01
      相关资源
      最近更新 更多