簡體   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