Jimmy lemieux
Jimmy lemieux

Reputation: 439

Loading a XIB file to a UIView Swift

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

Upvotes: 29

Views: 78252

Answers (14)

DEEPAK KUMAR
DEEPAK KUMAR

Reputation: 361

//**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

Upvotes: 1

Naresh
Naresh

Reputation: 17932

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 = .....

Upvotes: 1

Peter Combee
Peter Combee

Reputation: 595

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

Upvotes: 49

Ako
Ako

Reputation: 1005

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

Upvotes: 18

Giuseppe Mazzilli
Giuseppe Mazzilli

Reputation: 502

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.

Upvotes: 0

chainstair
chainstair

Reputation: 817

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

Upvotes: 2

DevAndArtist
DevAndArtist

Reputation: 5159

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.

Upvotes: 14

Aman Gupta
Aman Gupta

Reputation: 179

  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/

Upvotes: -1

Hooda
Hooda

Reputation: 1187

Swift 4.x

let myView = Bundle.main.loadNibNamed("yourXibView", owner: nil, options: nil)![0] as! UIView

Upvotes: 10

Can
Can

Reputation: 8571

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.

Upvotes: 4

Igor Kovryzhkin
Igor Kovryzhkin

Reputation: 2215

for swift 3

class YourClass:  UIView {
    class func instanceFromNib() -> YourClass {
        return UINib(nibName: "YourClassNibName", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! YourClass
    }
}

Upvotes: 7

Morgan Wilde
Morgan Wilde

Reputation: 17323

Using Swift 3.0

let viewFromNib: UIView? = Bundle.main.loadNibNamed("NibName", 
    owner: nil, 
    options: nil)?.first

Upvotes: 23

Andres Marin
Andres Marin

Reputation: 251

let xibView = NSBundle.mainBundle().loadNibNamed("NameXibView", owner: nil, options: nil)[0] as! UIView

Upvotes: 1

nsinvocation
nsinvocation

Reputation: 7637

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];

Upvotes: 3

Related Questions