代码之家  ›  专栏  ›  技术社区  ›  MuhsinFatih

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

  •  0
  • MuhsinFatih  · 技术社区  · 6 年前

    到目前为止,我找到的所有资源都建议使用此代码的变体从中加载视图 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公司 的设计)作为子视图。在这种情况下,如果我们向 self ,它将属于与中的UI元素不同的父元素 xib公司 文件

    因此层次结构如下所示:

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

    这不是不必要的包装吗?如果是,是否有方法在中添加所有视图 xib公司 文件收件人 自己 直接地或者,是否有一种与 xib公司 文件

    4 回复  |  直到 6 年前
        1
  •  1
  •   James Bucanek    6 年前

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

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

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

    这有点像黑客,因为它会缩短子控制器的视图加载生命周期(因为控制器从不加载自己的视图)。为了解决这个问题,您可以定义另一个属性,该属性将子控制器指向占位符视图或应将其自身插入其中的容器视图。然后更换 Bundle.main.loadNibName(blah, blah, blah) 只是 let replacement = self.view 并让视图控制器执行所有加载。


    这是一个完成后的解决方案,使用原始海报提供的故事板

    在情节提要中使用可重用视图
    添加一个 Container View (不要混淆 View )至 ViewController (您在情节提要上的主视图),并将其场景的(控制器)类设置为我们刚才定义的控制器( ReusableViewController )。
    gif
    就是这样。通过这种方式,您可以向视图中添加尽可能多的子视图,而无需将子视图包装到情节提要和代码中。
    容器视图 的预期用途实际上是为此目的而记录的 here

        2
  •  1
  •   Shehata Gamal    6 年前

    通常,在为视图实现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)
    
        3
  •  0
  •   sundance    6 年前

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

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

    这就是你想要的吗?

        4
  •  0
  •   barefeettom    5 年前

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

    无需摆弄文件所有者,无需将outlet连接到代理,无需以特殊方式修改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://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4

    汤姆