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

时间:2018-03-25 17:39:28

标签: ios swift xcode uiview uikit

到目前为止,我发现的所有资源都提供了此代码的变体,用于从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) } 文件加载视图,但始终实现相同的逻辑:

xib

这对我来说不合适,因为此函数属于UIView的子类,并将预期的设计(self的设计)添加为子视图。在这种情况下,如果我们向xib添加了不同的UI元素,它将属于与---self (UIView) ---Other UI elements added by code in `self` ---xib (UIView) ---UI elements in xib file 文件中的UI元素不同的父元素。

所以层次结构看起来像这样:

xib

这不是不必要的包装吗?如果是这样,有没有办法直接将self文件中的所有视图添加到xib?或者是否有一种不同的更优雅的方式从// Add a fee of 10.00 when Paypal is chosen add_action( 'woocommerce_cart_calculate_fees', 'custom_paypal_additional_fee', 20, 1 ); function custom_paypal_additional_fee( $cart ) { if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return; if( WC()->session->get( 'chosen_payment_method' ) == 'paypal' ) $cart->add_fee( __( 'Paypal fee', 'woocommerce' ), 10.00 ); } // Add the information on checkout paypal text gateways section add_filter('woocommerce_gateway_icon', 'custom_paypal_gateway_text', 20, 2 ); function custom_paypal_gateway_text( $html, $gateway_id ) { if( $gateway_id == 'paypal' ) $html .= ' <small class="paypal-fee">(+ '.wc_price(10.00).')</small>'; return $html; } // Enable ajax update checkout event when choosing a gateway to refresh the fee add_action('wp_footer', 'payment_gateways_update_checkout_event' ); function payment_gateways_update_checkout_event() { ?> <script type="text/javascript"> (function($){ $('form.checkout').on( 'change', 'input[name^="payment_method"]', function() { var t = { updateTimer: !1, dirtyInput: !1, reset_update_checkout_timer: function() { clearTimeout(t.updateTimer) }, trigger_update_checkout: function() { t.reset_update_checkout_timer(), t.dirtyInput = !1, $(document.body).trigger("update_checkout") } }; $(document.body).trigger('update_checkout') }); })(jQuery); </script> <?php } 文件加载设计?

4 个答案:

答案 0 :(得分:1)

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

}

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

这有点像黑客攻击,因为它会使子控制器的视图加载生命周期短路(因为控制器永远不会加载自己的视图)。要解决这个问题,您可以定义另一个属性,该子属性将子控制器指向占位符视图或应插入其自身的容器视图。然后用Bundle.main.loadNibName(blah, blah, blah)替换let replacement = self.view内容,让视图控制器完成所有加载。

这是一个完成的解决方案,使用原始海报提供的故事板,让它开始工作

在故事板中使用可重复使用的视图
Container View(不要与View混淆)添加到ViewController(故事板上的主视图),并将其场景(控制器)类设置为我们刚刚定义的控制器({{1 }})。
gif
而已。这样,您可以向视图添加任意数量的子视图,而无需在故事板和代码中包装子视图 ReusableViewController的预期用途实际上是为了这个目的而被记录here

答案 1 :(得分:0)

通常,当您为视图实现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)

答案 2 :(得分:0)

You can load the xib of a view during init:

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

Is that what your are looking for?

答案 3 :(得分:0)

这是您一直想要的答案。您可以只创建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://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4

汤姆