在为自定义视图调用 NSBundle(使用 loadNibName)后访问 IBOutlets 的最佳方法

Posted

技术标签:

【中文标题】在为自定义视图调用 NSBundle(使用 loadNibName)后访问 IBOutlets 的最佳方法【英文标题】:Best approach to access IBOutlets after calling NSBundle (with loadNibName) for a custom view 【发布时间】:2018-12-28 18:16:24 【问题描述】:

我正在寻找如何初始化通过 IBOutlets 连接的自定义视图的子视图(例如标签文本或按钮)的良好做法。

自定义视图的视图控制器在 init 上调用 xib 文件,如下所示:

final class MenuDiscoveryListView : NSView, MenuDiscoveryListViewProtocol 
    let C_NAME_XIB = "MenuDiscoveryList"

    @IBOutlet weak var labelStatus: NSTextField!
    @IBOutlet weak var stackList: NSStackView!

    var presenter : MenuDiscoveryListPresenter?

    override init(frame frameRect: NSRect) 
        super.init(frame: frameRect)
        xibInit(autoXibLoading: true)
    


    required init?(coder decoder: NSCoder) 
        super.init(coder: decoder)
        xibInit(autoXibLoading: false)
    


    /// Routine for initializating view
    ///
    /// - Parameter loadXib: Select if auto-load from related xib file into the view container is desired. Select TRUE, if function is called from NSView's  `init(frame frameRect: NSRect)`.
    func xibInit(autoXibLoading loadXib : Bool = true) 
        // Load xib item
        if loadXib 
            var topLevelObjects : NSArray?
            if Bundle(for: type(of: self)).loadNibNamed(C_NAME_XIB, owner: self, topLevelObjects: &topLevelObjects) 
                if let contentView = topLevelObjects!.first(where:  $0 is NSView ) as? NSView 
                    // Add loaded view from xib file into container as subview
                    self.addSubview(contentView)

                    // Transfer dimensions
                    self.frame = contentView.frame

                    // Define constraints
                    self.translatesAutoresizingMaskIntoConstraints = false
                    contentView.translatesAutoresizingMaskIntoConstraints = false

                    NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: contentView, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
                    NSLayoutConstraint(item: self, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true
                    NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
                    NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true
                
            
        
    

视图控制器的 init 以一种非常经典的方式从另一个视图控制器的 Presenter 模块中调用:

let view = MenuHeaderItemView()

但是,在初始化视图控制器后,正如预期的那样,IBOutlets 发现 nil。不过,我想在通过NSBundle(或Bundle's)loadNibName 初始化视图(例如标准字符串)后立即设置一个字符串值loadNibName,而无需等待awakeFromNib

同步执行此操作并在初始化后立即访问 IBOutlets 的良好做法或方法是什么?

编辑: 我已经意识到labelStatusstackList 已成功加载到contentView 中: 是否有任何优雅的方式将其内容/实例复制到 IBOutlets?

【问题讨论】:

【参考方案1】:

用语句

让 view = MenuHeaderItemView()

视图控制器尚未加载其视图层次结构/子视图。

据我了解,您可以使用:

let customView = Bundle.main.loadNibNamed("MenuDiscoveryList", owner: nil, 
options: nil)?[0] as? MenuDiscoveryListView

if let menuView = customView 
      menuView.labelStatus.text = "You label string"

谢谢,试试这个。

【讨论】:

感谢@Neelam,但至少在 Swift 4 中,loadNibNamed 返回一个 Bool。 不知道你有没有看过这个链接:***.com/questions/13534502/…我想解释的都在链接里。

以上是关于在为自定义视图调用 NSBundle(使用 loadNibName)后访问 IBOutlets 的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

在为自定义 DataGridViewColumn 设置属性时访问 DataGridView 控件

Swift:为自定义视图添加 Tap

为自定义活动指示器视图使 UIView 半透明

为自定义包装器视图添加示例视图到 PreviewProvider

将高度设置为自定义视图组

当我为自定义视图设置动画时,视图的高度会发生变化