繁体   English   中英

从xib加载视图的正确方法是什么?

[英]What is the correct way to load view from xib?

到目前为止,我发现的所有资源都提出了用于从xib文件加载视图的此代码的变体,但始终实现相同的逻辑:

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    var view = (Bundle.main.loadNibNamed("MyCustomView", owner: self, options: nil)![0])
    self.addSubview(view as! UIView)
}

在我看来,这不太合适,因为此函数属于UIView的子类,并将所需的设计( xib的设计)添加为子视图。 在这种情况下,如果我们将不同的UI元素添加到self ,则它将属于与xib文件中的UI元素不同的父xib

因此,层次结构如下所示:

---self (UIView)  
    ---Other UI elements added by code in `self`
    ---xib (UIView)
        ---UI elements in xib file

这不是不必要的包装吗? 如果是这样,是否有办法将xib文件中的所有视图直接添加到self 还是从xib文件中加载设计的方式有其他更优雅的方式?

重用一组复杂的子视图的一种方法是定义一个嵌入式视图控制器。 首先定义顶级视图控制器。 在其中,您定义了一个插座并将其连接到子控制器的一个实例(也在笔尖中定义)。 您还可以将子控制器的view连接到顶级笔尖的视图层次结构中的占位符UIView

class ViewController: UIViewController {

    @IBOutlet var childController: ReusableViewController?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
}

轻微的作用发生在子控制器中。 加载超级控制器时,将调用其awakeFromNib函数。 然后,子控制器使用与其连接的“占位符” UIView将其视图层次结构插入到顶级视图中。

class ReusableViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        // This controller manages a reusable view defined by a seperate nib.
        // When defined in the parent nib, its view is connected to a placeholder view object.
        // When the nib is loaded, the secondary nib is loaded and replaces the placeholder.
        let placeholder = self.view!
        Bundle.main.loadNibNamed("ReusableViewController", owner: self, options: nil)
        // (the nib connects the view property to the actual view, replacing the placeholder)
        placeholder.superview?.insertSubview(self.view, aboveSubview: placeholder)
        // (do something about constraints here?)
        placeholder.removeFromSuperview()
    }

}

这种安排的优点是,子控制器可以具有它需要的任何绑定,出口,动作,连接,属性和业务逻辑,可以整齐地封装和重用。

这有点骇人听闻,因为它会使子控制器的view-did-load生命周期短路(因为控制器从不加载自己的视图)。 为了解决这个问题,您可以定义另一个属性,该属性将子控制器指向应该插入自身的占位符视图或容器视图。 然后更换Bundle.main.loadNibName(blah, blah, blah)的东西只let replacement = self.view ,让视图控制器做所有的负荷。


这是使用原始海报提供的情节提要的最终解决方案,使之生效

在情节提要中使用可重用视图
Container View (不要与View混淆)添加到ViewController (情节ViewController的主视图),并将其场景的(控制器)类设置为我们刚刚定义的控制器( ReusableViewController )。
GIF
而已。 这样,您可以向视图中添加尽可能多的子视图,而不必将子视图包装在情节提要和代码中。
Container View的用途实际上是记录为正是为此目的在这里

通常,在为视图实现xib时,该xib始终包含该视图所需的所有元素。 但是如果有的话,您可以尝试添加这样的元素

var view = (Bundle.main.loadNibNamed("MyCustomView", owner: self, options: nil)![0]) as! UIView

var lbl = UILabel()

lbl.frame  = //.....

view.addSubview(lbl)

self.addSubview(view)

您可以在初始化期间加载视图的xib:

init() {
    super.init(nibName: "ViewController", bundle: Bundle(identifier: "domain.AppName")!)
    self.loadView()
}

那是您要找的东西吗?

这就是您一直想要的答案。 您可以只创建CustomView类,将其主实例包含在带有所有子视图和出口的xib中。 然后,您可以将该类应用于情节提要或其他xib中的任何实例。

无需摆弄File的所有者,或将插座连接到代理或以特殊的方式修改xib,或将自定义视图的实例添加为其自身的子视图。

只要这样做:

  1. 导入BFWControls框​​架
  2. 将您的超类从UIView更改为NibView (或从UITableViewCell更改为NibTableViewCell

而已!

它甚至可以与IBDesignable一起使用,以在设计时在情节提要中引用您的自定义视图(包括来自xib的子视图)。

您可以在此处了解更多信息: https : //medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155

您可以在此处获取开源的BFWControls框​​架: https : //github.com/BareFeetWare/BFWControls

这是驱动它的NibReplaceable代码的简单摘录,以防您好奇: https : NibReplaceable

汤姆👣

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM