29

I am trying to load my XIB file into a UIView but I am having some trouble. I have the required override functions but they seem to be crashing. Saying this error, warning:

could not load any Objective-C class information. This will significantly reduce the quality of type information available.

I was wondering if someone could show me how to properly load the XIB file into a UIView

import UIKit

class Widget: UIView {

    let view = UIView()

    override init(frame: CGRect) {
        super.init(frame: frame)

        //call function

        loadNib()

    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        loadNib()

        //fatalError("init(coder:) has not been implemented")
    }

    func loadNib() {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: "nib", bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        self.addSubview(view);  
    }
}
Naresh
  • 16,698
  • 6
  • 112
  • 113
Jimmy lemieux
  • 439
  • 1
  • 5
  • 14

14 Answers14

49

I uses this in one of our projects, might be useful to you

import UIKit

class RegisterPageView: UIView {
    
        class func instanceFromNib() -> RegisterPageView {
            return UINib(nibName: "RegisterPageView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! RegisterPageView
        }
}
 
Ronak Patel
  • 609
  • 4
  • 16
Peter Combee
  • 595
  • 4
  • 10
23

Using Swift 3.0

let viewFromNib: UIView? = Bundle.main.loadNibNamed("NibName", 
    owner: nil, 
    options: nil)?.first
Morgan Wilde
  • 16,795
  • 10
  • 53
  • 99
18

Improved DevAndArtist UIView extension

public extension UIView
{
    static func loadFromXib<T>(withOwner: Any? = nil, options: [UINib.OptionsKey : Any]? = nil) -> T where T: UIView
    {
        let bundle = Bundle(for: self)
        let nib = UINib(nibName: "\(self)", bundle: bundle)

        guard let view = nib.instantiate(withOwner: withOwner, options: options).first as? T else {
            fatalError("Could not load view from nib file.")
        }
        return view
    }
}

Usage

let view = CustomView.loadFromXib()
let view = CustomView.loadFromXib(withOwner: self)
let view = CustomView.loadFromXib(withOwner: self, options: [UINibExternalObjects: objects])

External Objects discussion

Alec O
  • 1,697
  • 1
  • 18
  • 31
Ako
  • 956
  • 1
  • 10
  • 13
14

Here is my approach (written in Swift 3.1):

protocol XibDesignable : class {}

extension XibDesignable where Self : UIView {

    static func instantiateFromXib() -> Self {

        let dynamicMetatype = Self.self
        let bundle = Bundle(for: dynamicMetatype)
        let nib = UINib(nibName: "\(dynamicMetatype)", bundle: bundle)

        guard let view = nib.instantiate(withOwner: nil, options: nil).first as? Self else {

            fatalError("Could not load view from nib file.")
        }
        return view
    }
}

extension UIView : XibDesignable {}

Now I simply can create any UIView subclass from a Xib (assuming there is one) like so MyCustomView.instantiateFromXib(). Remember to name your Xib file exactly as your UIView subclass and set the type of the main view in that Xib file correctly.


As soon as SE-0068 will be implemented one could drop the protocol and move the function directly into the UIView extension.


Just a note: The original post uses a commonly used pattern with a nested view. IMHO this is a bad pattern which does not utilize the resources and only creates unnecessary view hierarchy.

DevAndArtist
  • 4,971
  • 1
  • 23
  • 48
  • I like this approach. In my case, .xib files contain only one view of the current class and `guard let view = nib.instantiate(withOwner: nil, options: nil).last` crashed, since `.last` returned nil. For some reason, `guard let view = nib.instantiate(withOwner: nil, options: nil)[0]` works great though. Just change `.last` to `[0]` – m_katsifarakis Oct 16 '17 at 11:04
  • 1
    There is no `.last` in my sample. Furthermore `.first` cannot be nil if the array really contains something. `[0]` will also crash if the array is empty. – DevAndArtist Oct 16 '17 at 11:17
  • @DevAndArtist I have used your code and I'm facing a memory leak issue. – Pradip Sutariya Nov 22 '21 at 11:41
10

Swift 4.x

let myView = Bundle.main.loadNibNamed("yourXibView", owner: nil, options: nil)![0] as! UIView
Hooda
  • 1,157
  • 1
  • 9
  • 16
7

for swift 3

class YourClass:  UIView {
    class func instanceFromNib() -> YourClass {
        return UINib(nibName: "YourClassNibName", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! YourClass
    }
}
Igor Kovryzhkin
  • 2,195
  • 1
  • 27
  • 22
  • Can you fill some more blanks in? Where does that go and where does it get called? I've been mucking around with this longer than I care to admit and everything I've found is either out of date Swift or Objective-C. Seems like it's a simple exercise, but I'm too ****ing dumb to figure it out. – Adrian May 19 '17 at 22:53
4

In my project I implemented the following (very similar to Peter's Solution)

import UIKit

// MARK: - Protocol Declaration

public protocol InterfaceBuilderInstantiable
{
    /// The UINib that contains the view
    ///
    /// Defaults to the swift class name if not implemented
    static var associatedNib : UINib { get }
}

// MARK: - Default Implementation

extension InterfaceBuilderInstantiable
{
    /// Creates a new instance from the associated Xib
    ///
    /// - Returns: A new instance of this object loaded from xib
    static func instantiateFromInterfaceBuilder() -> Self
    {
        return associatedNib.instantiate(withOwner:nil, options: nil)[0] as! Self
    }

    static var associatedNib : UINib
    {
        let name = String(describing: self)
        return UINib(nibName: name, bundle: Bundle.main)
    }
}

To use, you just simply implement the protocol:

class MyView: UIView, InterfaceBuilderInstantiable
{
    // The rest

And if your nib is the same name as your class (MyView.xib), you're set: the default implementation of the protocol looks for a nib with the same name as the class in the main bundle.

Of course, if your nib is in another bundle or has a different name you can override the associatedNib and return your own nib.

Can
  • 8,502
  • 48
  • 57
3

Usually I use the following way to load a xib file owned by a custom UIView:

NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0];
nsinvocation
  • 7,559
  • 3
  • 41
  • 46
2

Swift 4.x

This is finally how I did it This is not in the customView itself. I put the code where the ViewController is loading the customView.

import UIKit

class StartMenuViewController: UIViewController {

    @IBOutlet weak var customView: CustomView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let myView = Bundle.main.loadNibNamed("CustomView", owner: self, options: nil)![0] as! UIView
        customView .addSubview(myView)
    }
chainstair
  • 681
  • 8
  • 18
1
let xibView = NSBundle.mainBundle().loadNibNamed("NameXibView", owner: nil, options: nil)[0] as! UIView
Andres Marin
  • 251
  • 3
  • 12
1

Swift 5.x

let loadMusicView = Bundle.main.loadNibNamed("MusicView", owner: nil, options: nil)![0] as? MusicView
loadMusicView?.frame = controlsMainView.bounds
loadMusicView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
controlsMainView.addSubview(loadMusicView!)

//if you have variables in your .xib file access those variables like this
loadMusicView.yourVariableName = .....
Naresh
  • 16,698
  • 6
  • 112
  • 113
0

I would like to share this piece of code that required me some effort to make it resilient.

import Foundation

protocol Nib {

    func registerNib()

}

extension Nib where Self : UIView {

    func registerNib() {
        guard let nibName = type(of: self).description().components(separatedBy: ".").last else { return }
        // ** Check if resource is used in Interface Builder first to avoid crash during compile
        #if !TARGET_INTERFACE_BUILDER
        let bundle = Bundle(for: type(of: self))
        guard let _ = bundle.path(forResource: nibName, ofType: "nib")
            else { fatalError("can't find \(nibName) xib resource in current bundle") }
        #endif
        guard let view = Bundle(for: type(of: self)).loadNibNamed(nibName, owner: self, options: nil)?.first as? UIView
            else { return }
        // ** Another way to write it but do not work if xib is bundled with framework
        //guard let view = UINib(nibName: nibName, bundle: nil).instantiate(withOwner: self, options: nil).first as? UIView
        //    else { return }
        view.frame = bounds
        addSubview(view)
    }

}

You can use this as follow creating a xib resource file named as class name (aka CustomView.xib)

import UIKit

class CustomView: UIView, Nib {

    override init(frame: CGRect) {
        super.init(frame: frame)
        postInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        postInit()
    }

    func postInit() {
        registerNib()
    }

}

Do not forget to set xib resource file's owner class to CustomView and leave blank custom class class field.

0

//**Just use this class as super class for the view **

import UIKit

class ViewWithXib: UIView {

func initUI() {}

private func xibSetup() {
    let view = loadViewFromNib()
    view.frame = bounds
    view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
    addSubview(view)
    initUI()
}

private func loadViewFromNib() -> UIView {
    let thisName = String(describing: type(of: self))
    let view = Bundle(for: self.classForCoder).loadNibNamed(thisName, owner: self, options: nil)?.first as! UIView
    return view
}


override init(frame: CGRect) {
    super.init(frame: frame)
    xibSetup()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    xibSetup()
}

}

// Usage

class HeaderView: ViewWithXib {
}


let header = HeaderView() // No need to load the view from nib, It will work
DEEPAK KUMAR
  • 341
  • 4
  • 8
-1
  func configureNib() -> UIView {
    let bundle = Bundle(for: type(of: self))
    let nib = UINib(nibName: "CustomUIView", bundle: bundle)
    let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
    return view
}

And use this tutorial for custom view with xib... https://developerfly.com/custom-view-use-xib-swift/

Aman Gupta
  • 159
  • 1
  • 8