sumesh
sumesh

Reputation: 2249

add custom view from nib to viewcontroller

I have a viewcontroller with a custom view area on the top, as seen in the below image enter image description here

I want to load a custom xib into this area and I am struggling to do so. I created a ABC.swift file which is a subclass of NSView and a .xib file with the same name. In the .xib file from the identity inspector I set the class ABC. Then in my viewcontroller's viewDidLoad() I try to do the following

let abcView = Bundle.main.loadNibNamed("ABC", owner: self, topLevelObjects: nil) as ABC

I also created an outlet for the custom view in my vc then in viewDidLoad()

customView.addSubview(abcView)

But this not working as abcView is of type Bool. Can anyone tell me what am I doing wrong? What is the best way to achieve my goal?

Upvotes: 2

Views: 3276

Answers (3)

Yaser
Yaser

Reputation: 408

A slightly more compact version of answer already given

extension NSView {

    static func loadFromNib(nibName: String, owner: Any? = nil) -> NSView? {
        var arrayWithObjects: NSArray?
        let nibLoaded = Bundle.main.loadNibNamed(NSNib.Name(rawValue: nibName),
                                                 owner: owner,
                                                 topLevelObjects: &arrayWithObjects)

        if nibLoaded {
            return arrayWithObjects?.first(where: { $0 is NSView }) as? NSView
        }

        return nil
    }
}

Upvotes: 0

Siddharth Bhatt
Siddharth Bhatt

Reputation: 613

I use the following Extension on NSView : (same as answer given by user Accepted Answer)

import Foundation
import Cocoa

extension NSView {

    static func loadFromNib(nibName: String, owner: Any?) -> NSView? {

        var arrayWithObjects: NSArray?

        let nibLoaded = Bundle.main.loadNibNamed(NSNib.Name(rawValue: nibName), owner: owner, topLevelObjects: &arrayWithObjects)

        if nibLoaded {
            guard let unwrappedObjectArray = arrayWithObjects else { return nil }
            for object in unwrappedObjectArray {
                if object is NSView {
                    return object as? NSView
                }
            }
            return nil
        } else {
            return nil
        }
    }
}

This is how to use it in loadView() of an NSViewController :

if let loadedNavButtons: NavigationButtonsView = NSView.loadFromNib(nibName: "NavigationButtonsView", owner: self) as? NavigationButtonsView {
    // Do whatever you want with loadedNavButtons
}

Works in Swift 4 , XCode 9.2

Upvotes: 3

Accepted Answer
Accepted Answer

Reputation: 188

As of this writing, macOS only has a legacy means of bundle loading: func loadNibNamed(_ nibName: NSNib.Name, owner: Any?, topLevelObjects: AutoreleasingUnsafeMutablePointer<NSArray?>?) -> Bool.

It shall pass the top-level objects of the nib in the array you supplied as the last argument:

var objs = NSArray()
Bundle.main.loadNibNamed("MyScreen", owner: self, topLevelObjects: &objs)

Be sure to check it returned true. Otherwise it means it could not load the desired nib.

After it's loaded, the objects in the left-hand menu in the Interface Builder are available in your code. If you have a View in your nib, you can add it as a subview:

for obj in objs {
  if obj is MyViewClass {
    // Now we are sure that the obj is really a view of the desired class
    self.someView.addSubview(obj)
  }
}

Upvotes: 0

Related Questions