2

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?

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
sumesh
  • 2,179
  • 2
  • 21
  • 37
  • Duplicate: https://stackoverflow.com/questions/27228362/os-x-addsubview-from-xib-in-swift?rq=1 – El Tomato Aug 08 '17 at 11:58
  • @ElTomato i dont know how to convert it to swift 3 syntax. also i m a bit confused between the connections once has to make with the storyboard. `let abcView = Bundle.main.loadNibNamed("ABC", owner: self, topLevelObjects: nil)` returns bool. How can i add this to subview ? – sumesh Aug 08 '17 at 14:44

3 Answers3

3

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

Siddharth Bhatt
  • 613
  • 6
  • 7
0

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)
  }
}
Accepted Answer
  • 188
  • 2
  • 7
  • so is there a better way to do what i want to do ? – sumesh Aug 08 '17 at 17:42
  • @sumesh, well, you can use the same storyboard for everything and try something like this: https://developer.apple.com/documentation/appkit/nsstoryboard/1426549-instantiatecontroller – Accepted Answer Aug 09 '17 at 05:08
0

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
    }
}
Yaser
  • 408
  • 2
  • 6