0

I am looking for good practice how to initialize subviews (e.g. text of labels, or buttons) of a custom view that are connected via IBOutlets.

The custom view's view controller is calling the xib file on init like this:

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

The init of the view controller is called from another view controller's presenter module in a very classy way:

let view = MenuHeaderItemView()

However, after initializing the view controller, as expected, the IBOutlets found nil. Nevertheless, I wanted to set a string value of labelStatus right after initializing the view (e.g. standard string) through NSBundle's (or Bundle's) loadNibName without waiting for awakeFromNib.

What is a good practice or approach to do this synchronously and access the IBOutlets right after the init?

EDIT: I have realized that labelStatus and stackList are successfully loaded in contentView: enter image description here Is there any elegant way to copy their content/instantiation over to the IBOutlets?

Maschina
  • 755
  • 7
  • 30

1 Answers1

0

with the statement

let view = MenuHeaderItemView()

The view controller has not yet loaded its view hierarchy/ Subviews.

From my understanding you may use:

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

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

Thanks, have a try with this.

Neelam Verma
  • 3,232
  • 1
  • 22
  • 33
  • Thanks @Neelam, but at least in Swift 4, loadNibNamed returns a Bool. – Maschina Dec 28 '18 at 20:14
  • I don't know if you have looked into this link: https://stackoverflow.com/questions/13534502/ios-loadnibnamed-confusion-what-is-best-practice?rq=1 Whatever i want to explain it is in the link. – Neelam Verma Dec 29 '18 at 21:34