184

Here is my Objective-C code which I'm using to load a nib for my customised UIView:

-(id)init{

    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"myXib" owner:self options:nil];
    return [subviewArray objectAtIndex:0];

}

What is the equivalent code in Swift?

Cesare
  • 9,139
  • 16
  • 78
  • 130
Bagusflyer
  • 12,675
  • 21
  • 96
  • 179

30 Answers30

400

My contribution:

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

Then call it like this:

let myCustomView: CustomView = UIView.fromNib()

..or even:

let myCustomView: CustomView = .fromNib()
Community
  • 1
  • 1
Robert Gummesson
  • 5,630
  • 2
  • 17
  • 16
  • 25
    Best answer by far. – CodyMace Jan 26 '17 at 23:55
  • 8
    **Best** answer here. Clean and Simple –  Jan 31 '17 at 19:22
  • @Robert how about the button click events? Like I have button in my nib and I load it like you described above, How can handle the IBAction of my button click here in this case? – Qadir Hussain Feb 21 '17 at 15:09
  • @QadirHussain - I suggest you ask that as a separate question. That's not really related to this question or answer. The short answer is you handle it the same way as any other IBActions. – Robert Gummesson Feb 21 '17 at 17:11
  • In my case as i defined Nib's file owner to a custom class the **String(describing: T.self)** was returning _UIView_ so I made this run with this **let nibName = NSStringFromClass(self).components(separatedBy: ".").last!**. – BuguiBu Mar 14 '17 at 08:43
  • @Robert how about also change `[0]` to `.first`? – Yuchen Jun 21 '17 at 14:03
  • 4
    @YuchenZhong - I prefer [0] over .first as that would return an optional. If you force unwrap it, it wouldn't be safer. ...and this begs the question: Why not return an optional as some of the above solutions? Answer: You can. Nothing wrong with that. But... if it would ever return nil, the name of the xib/class do not match. This is a developer mistake and should be caught immediately and never make it to production. Here I would prefer to have the app crash over leaving it in some weird state. Just my 2 cents / preference. – Robert Gummesson Jun 21 '17 at 16:29
  • @RobertGummesson Okay, fair enough :) that's personal preference I guess. I find it a bit more readable with `.first`. And I agree that if it ever returns `nil`, that is a developer mistake and should be reported/crash as soon as possible. – Yuchen Jun 21 '17 at 18:27
  • @RobertGummesson I wonder how can `UIView.fromNib()` return `CustomView `, shouldn't it be `let myCustomView: CustomView = CustomView.fromNib()`? – allenlinli Jun 30 '17 at 03:48
  • 1
    @allenlinli - The method is a static extension of UIView as supposed to CustomView. It works because the compiler infers type using the explicit type annotation. Because CustomView is a subclass of UIView and the type has already been inferred, we don’t need to infer it again, UIView can therefore be omitted as shown in my second example. Having that said, you could obviously make the call the way you put it down too. – Robert Gummesson Jun 30 '17 at 06:47
  • Best answer here. Expanding on allenlinli's point, interestingly, if you don't initialize the view inline (i.e. `let customView = .fromNib()`) then you will have to cast it as your subclass (`self.customView = CustomView.fromNib() as CustomView`). or the compiler complains: "Cannot assign value of type 'UIView' to type 'CustomView'". Unexpected! since the compiler should have inferred it. I actually think this is a compiler bug, because in order to fix the problem you do a simple cast using `as` as opposed to the more typical forced downcast using `as!`. – MH175 Mar 25 '18 at 19:45
  • How can you do this for views in other bundles? – MH175 Mar 25 '18 at 20:07
  • 5
    This solution didn't work for me for the case when there was a custom view inside the .xib. I would suggest to fix this part to: return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)![0] as! T – Nadzeya Apr 18 '18 at 17:54
  • How do you do when you want to pass parameters to your initializer ? My view has some params, that are nor optionnal, nor defined inside the view. How can I load it with your method, since I can't assign self in Swift ? – bbusseuil Dec 20 '18 at 00:22
  • This approach seems to be having issue with Swift 4.2. See my answer using Swift 4 protocol extensions, I believe this is more flexible. – Brody Robertson Dec 26 '18 at 23:04
  • @BrodyRobertson - It's working fine for me in Swift 4.2. What issue are you referring to? – Robert Gummesson Jan 02 '19 at 09:06
  • Works great, except in my case I wanted to load a UIViewController (isn't that more often the case?). Anyway, I 'global replaced' UIView with UIViewController in the given code and voila! Thanks @RobertGummesson – Snips Mar 15 '20 at 07:03
  • The problem with this extension is you have to explicitly mention the type of the variable or constant as `CustomView`. With https://stackoverflow.com/a/60923026/397915 answer you don't have to. – iCoder Mar 30 '20 at 02:05
  • Why this not works like that?: `let myCustomView = CustomView.fromNib()`. Why compiler can't infer generic type? – HammerSlavik May 08 '20 at 15:28
  • To avoid casting view to your custom type, use this instead: `extension UIView { class func loadFromNib() -> Self { return Bundle(for: Self.self).loadNibNamed(String(describing: Self.self), owner: nil, options: nil)![0] as! Self } }` – Okhan Okbay Oct 24 '20 at 21:57
  • @HammerSlavik Compiler can't infer the type cause you are not giving it a hint. All you define is T being a UIView, nothing more. If you cast the returning view where in you call the fromNib function, then compiler understands the type you want to cast the view into. – Okhan Okbay Oct 24 '20 at 22:00
  • doesn't work at all - `String(describing: T.self)` always return "UIView" nib name – Gargo May 11 '23 at 07:23
  • @Gargo - Not if you infer the type. You can see the usage in the example snippets. – Robert Gummesson Jun 22 '23 at 12:13
193

Original Solution

  1. I created a XIB and a class named SomeView (used the same name for convenience and readability). I based both on a UIView.
  2. In the XIB, I changed the "File's Owner" class to SomeView (in the identity inspector).
  3. I created a UIView outlet in SomeView.swift, linking it to the top level view in the XIB file (named it "view" for convenience). I then added other outlets to other controls in the XIB file as needed.
  4. in SomeView.swift, I loaded the XIB inside the "init with code" initializer. There is no need to assign anything to "self". As soon as the XIB is loaded, all outlets are connected, including the top level view. The only thing missing, is to add the top view to the view hierarchy:

.

class SomeView: UIView {
   required init(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      NSBundle.mainBundle().loadNibNamed("SomeView", owner: self, options: nil)
      self.addSubview(self.view);    // adding the top level view to the view hierarchy
   }
   ...
}

Note that this way I get a class that loads itself from nib. I could then use SomeView as a class whenever UIView could be used in the project (in interface builder or programmatically).

Update - using Swift 3 syntax

Loading a xib in the following extension is written as an instance method, which can then be used by an initializer like the one above:

extension UIView {

    @discardableResult   // 1
    func fromNib<T : UIView>() -> T? {   // 2
        guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else {    // 3
            // xib not loaded, or its top view is of the wrong type
            return nil
        }
        self.addSubview(contentView)     // 4
        contentView.translatesAutoresizingMaskIntoConstraints = false   // 5 
        contentView.layoutAttachAll(to: self)   // 6 
        return contentView   // 7
    }
}
  1. Using a discardable return value since the returned view is mostly of no interest to caller when all outlets are already connected.
  2. This is a generic method that returns an optional object of type UIView. If it fails to load the view, it returns nil.
  3. Attempting to load a XIB file with the same name as the current class instance. If that fails, nil is returned.
  4. Adding the top level view to the view hierarchy.
  5. This line assumes we're using constraints to layout the view.
  6. This method adds top, bottom, leading & trailing constraints - attaching the view to "self" on all sides (See: https://stackoverflow.com/a/46279424/2274829 for details)
  7. Returning the top level view

And the caller method might look like this:

final class SomeView: UIView {   // 1.
   required init?(coder aDecoder: NSCoder) {   // 2 - storyboard initializer
      super.init(coder: aDecoder)
      fromNib()   // 5.
   }
   init() {   // 3 - programmatic initializer
      super.init(frame: CGRect.zero)  // 4.
      fromNib()  // 6.
   }
   // other methods ...
}
  1. SomeClass is a UIView subclass that loads its content from a SomeClass.xib file. The "final" keyword is optional.
  2. An initializer for when the view is used in a storyboard (remember to use SomeClass as the custom class of your storyboard view).
  3. An initializer for when the view is created programmatically (i.e.: "let myView = SomeView()").
  4. Using an all-zeros frame since this view is laid out using auto-layout. Note that an "init(frame: CGRect) {..}" method is not created independently, since auto-layout is used exclusively in our project.
  5. & 6. Loading the xib file using the extension.

Credit: Using a generic extension in this solution was inspired by Robert's answer below.

Edit Changing "view" to "contentView" to avoid confusion. Also changed the array subscript to ".first".

Alex Nolasco
  • 18,750
  • 9
  • 86
  • 81
GK100
  • 3,664
  • 1
  • 26
  • 19
  • 7
    Setting the class name to `File's Owner` hit the spot... Thanks! – Aviel Gross Feb 04 '15 at 09:59
  • Thanks a million. I was stuck. You basically did this procedure "http://www.maytro.com/2014/04/27/building-reusable-views-with-interface-builder-and-auto-layout.html" but in SWIFT. – Joe C Mar 04 '15 at 19:04
  • 16
    UIView doesn't have property view, so calling self.view causes an error – Nastya Gorban Apr 15 '15 at 08:39
  • 2
    Do not set the views Class. – Michael Apr 20 '15 at 21:28
  • 4
    @NastyaGorban self.view actually refers in this case to the outlet property (named "view) that GK100 linked from the top level view in the .xib to SomeView.swift. Not adding that outlet will give you error as there is no "view" property in NSView classes as you say. – Ausiàs May 13 '15 at 01:08
  • 3
    I am getting crash when loading nib(loadNibNamed). Using Xcode 6.3 and Swift – karthikPrabhu Alagu Aug 06 '15 at 07:17
  • 1
    2GK100: What is "SomeObject". It should be "SomeView"? – vvkatwss vvkatwss Sep 22 '15 at 06:56
  • Good Catch. It is "SomeView". I fixed the answer. – GK100 Oct 12 '15 at 01:11
  • Instead of creating a 'view' container, it can be named 'contentView' (like how it is done with UITableViewCell inline with Cocoa conventions. – srik Mar 07 '16 at 15:19
  • Excellent idea! Thanks, this is the best solution I found so far. – Bartosz Olszanowski Nov 27 '16 at 18:49
  • @GK100, "fatal error: unexpectedly found nil while unwrapping an Optional value" When I bind outlet from .xib to UIView class. When I run the app the above error is showing at every time. Any idea about that what am I doing wrong. – Gautam Sareriya Apr 17 '17 at 11:22
  • @Gautam Sareriya, You should only set the class (SomeView) to "File's Owner", not on the view itself. If there are outlets, link them to File's Owner too. See Step 6 of this tutorial: http://supereasyapps.com/blog/2014/12/15/create-an-ibdesignable-uiview-subclass-with-code-from-an-xib-file-in-xcode-6 – yuji May 05 '17 at 03:05
  • 3
    I missed the description of layoutAttachAll and I thought it's a method that got renamed or something, and found an implementation in this question if anyone needs it: https://stackoverflow.com/q/41851892/555516 – Vlad V Jul 19 '17 at 18:16
  • @GK100 if we want to instantiate a view controller (by clicking button inside the viw) from this class someview: UIView , then how can we do that? – ArgaPK Nov 29 '17 at 12:30
  • 17
    calling `fromNib()` from within `init(coder aDecoder: NSCoder)` creates an infinite loop as loading the Nib inside the `fromNib()` method makes a call to: `init(coder aDecoder: NSCoder)` – Matthew Cawley Dec 15 '17 at 01:46
  • 2
    Did anyone find the loop mentioned by @MatthewCawley? – CSjunkie Apr 24 '19 at 17:01
  • @CSjunkie - Calling the method `fromNib()` tells the view to load itself from a xib file of the same name as the class. When loading views from a xib file or storyboard, `init?(coder aDecoder: NSCoder)` is called. Therefore if you call `fromNib()` inside the `init?(coder aDecoder: NSCoder)` method, the loading of the view from a xib file will recall the `init?(coder aDecoder: NSCoder)` method causing an infinite loop. – Matthew Cawley Apr 25 '19 at 01:50
  • 1
    @MatthewCawley Typo on my part, I meant did anyone find the solution. I implemented a workaround by putting the view in a container view and calling `fromNib()` there but this results in an extra view. – CSjunkie Apr 25 '19 at 18:43
  • 2
    @CSjunkie - The work around is to simply not call `fromNib()` in the `init?(coder aDecoder: NSCoder)` method. :) – Matthew Cawley Apr 26 '19 at 08:47
  • For me this was not working. I followed https://stackoverflow.com/a/61243777/10505343 approach. Think this will help.. – Wimukthi Rajapaksha Apr 16 '20 at 06:23
  • For those getting an infinite loop like @MatthewCawley, make sure the custom class name in the interface designer for the top level view is not set to your custom class but is left as the default UIView. The custom class is defined at the File Owner level only. – Maximvs Feb 10 '22 at 21:42
94

Now being able to return -> Self in swift helps simplify this a bit. Last confirmed on Swift 5.

extension UIView {
    class func fromNib(named: String? = nil) -> Self {
        let name = named ?? "\(Self.self)"
        guard
            let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil)
            else { fatalError("missing expected nib named: \(name)") }
        guard
            /// we're using `first` here because compact map chokes compiler on
            /// optimized release, so you can't use two views in one nib if you wanted to
            /// and are now looking at this
            let view = nib.first as? Self
            else { fatalError("view of type \(Self.self) not found in \(nib)") }
        return view
    }
}

If your .xib file and subclass share the same name, you can use:

let view = CustomView.fromNib()

If you have a custom name, use:

let view = CustomView.fromNib(named: "special-case")

NOTE:

If you're getting the error "view of type YourType not found in.." then you haven't set the view's class in the .xib file

Select your view in the .xib file, and press cmd + opt + 4 and in the class input, enter your class

Logan
  • 52,262
  • 20
  • 99
  • 128
  • This is the best solution IMO! Clean and works perfectly! – Shaikh Sonny Aman Jul 06 '15 at 10:45
  • 2
    I cannot get this to work under XCode 7.1 beta 3 - not sure if it's a beta thing but basically I've tried every way to create a custom view directly from a nib in Swift and I always get the same result: the class it's creating is not KVC compliant with the outlets. Not sure if it's something I'm doing wrong but my class is pretty simple and the File's Owner is correct. I used to do this all the time under Objective-C. – Echelon Oct 21 '15 at 19:37
  • That's very odd, is it this particular code, or loading the xib in general. Can you post / link to some code? – Logan Oct 21 '15 at 19:48
  • I can see some problems here: if you set your custom class to File's owner, then you cannot instantiate with owner = nil. If you set to root view in XIB, then you cannot use it from other XIB's/Storyboard, because you have to call custom loading from `required init?(coder aDecoder: NSCoder)` – Nikita Took Nov 18 '15 at 14:14
  • @NikitaTook Not sure I see how those are problems that are related to this answer? If owner = nil causes a failure, that's a requirement imposed by the user that is problematic in any nib loading answer. Using in other storyboards is generally problematic in its own right, and again isn't really affected by this answer. As it stands, this seems like a list of nib limitations that are independent of my answer. Perhaps I'm wrong, could you clarify how this impacts things with an explanation of what you see as fixable? – Logan Nov 18 '15 at 14:48
  • 2
    @Logan it's not really related to your code, but imo custom views should support loading from Storyboard/XIB. My comment was just a notification for those, who want to create such views – Nikita Took Nov 19 '15 at 20:15
  • @Logan I just revisited this and it works again in XCode 7.2 - thanks, this is the best solution to the problem! (Quite why Apple have made this so difficult beats me.) – Echelon Dec 22 '15 at 14:35
  • 1
    Note I still have a problem using the second form of calling this function, namely `let myCustomView = UIView.fromNib() as? CustomView`. In this case, `T.self` resolves to `UIView` rather than `CustomView` and it fails to find the nib. I am not sure why this is - maybe the inferred type for the `let` means the function is called as a `UIView`? – Echelon Dec 22 '15 at 15:08
  • 2
    It is important to point out that trying to use File's Owner to hook up the outlets (as we did in the good olde days) will cause this to crash. In IB, the File's Owner must be nil/empty and the outlets should be hooked up to the view instead. – Echelon Dec 22 '15 at 16:07
  • 1
    @Echelon you saved my day!!! I connected my outlets using the File's Owner and it didn't work, using the view instead worked. – Jan Erik Schlorf Oct 09 '18 at 07:29
27

Swift 4 - 5.1 Protocol Extensions

public protocol NibInstantiatable {
    
    static func nibName() -> String
    
}

extension NibInstantiatable {
    
    static func nibName() -> String {
        return String(describing: self)
    }
    
}

extension NibInstantiatable where Self: UIView {
    
    static func fromNib() -> Self {
        
        let bundle = Bundle(for: self)
        let nib = bundle.loadNibNamed(nibName(), owner: self, options: nil)
        
        return nib!.first as! Self
        
    }
    
}

Adoption

class MyView: UIView, NibInstantiatable {

}

This implementation assumes that the Nib has the same name as the UIView class. Ex. MyView.xib. You can modify this behavior by implementing nibName() in MyView to return a different name than the default protocol extension implementation.

In the xib the files owner is MyView and the root view class is MyView.

Usage

let view = MyView.fromNib()
Brody Robertson
  • 8,506
  • 2
  • 47
  • 42
22

try following code.

var uiview :UIView?

self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView

Edit:

import UIKit

class TestObject: NSObject {

     var uiview:UIView?

    init()  {
        super.init()
       self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
    }


}
Jasmin
  • 794
  • 7
  • 18
15

If you have a lot of custom views in your project you can create class like UIViewFromNib

Swift 2.3

class UIViewFromNib: UIView {
    
    var contentView: UIView!
    
    var nibName: String {
        return String(self.dynamicType)
    }
    
    //MARK:
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        loadViewFromNib()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        loadViewFromNib()
    }
    
    //MARK:
    private func loadViewFromNib() {
        contentView = NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0] as! UIView
        contentView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

Swift 5

class UIViewFromNib: UIView {
    
    var contentView: UIView!
    
    var nibName: String {
        return String(describing: type(of: self))
    }
    
    //MARK:
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        loadViewFromNib()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        loadViewFromNib()
    }
    
    //MARK:
    func loadViewFromNib() {
        let bundle = Bundle(for: UIViewFromNib.self)
        contentView = UINib(nibName: nibName, bundle: bundle).instantiate(withOwner: self).first as? UIView
        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

And in every class just inherit from UIViewFromNib, also you can override nibName property if .xib file has different name:

class MyCustomClass: UIViewFromNib {
    
}
ChikabuZ
  • 10,031
  • 5
  • 63
  • 86
12

I achieved this with Swift by the following code:

class Dialog: UIView {
    @IBOutlet var view:UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.frame = UIScreen.mainScreen().bounds
        NSBundle.mainBundle().loadNibNamed("Dialog", owner: self, options: nil)
        self.view.frame = UIScreen.mainScreen().bounds
        self.addSubview(self.view)
    }

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

Don't forget to connect your XIB view outlet to view outlet defined in swift. You can also set First Responder to your custom class name to start connecting any additional outlets.

Hope this helps!

Amr Hossam
  • 2,313
  • 1
  • 22
  • 23
11

Tested in Xcode 7 beta 4 , Swift 2.0 and iOS9 SDK . The following code will assign xib to the uiview. You can able to use this custom xib view in storyboard and access the IBOutlet object also.

import UIKit

@IBDesignable class SimpleCustomView:UIView
{
    var view:UIView!;

    @IBOutlet weak var lblTitle: UILabel!

   @IBInspectable var lblTitleText : String?
        {
        get{
            return lblTitle.text;
        }
        set(lblTitleText)
        {
            lblTitle.text = lblTitleText!;
        }
    }

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadViewFromNib ()
    }
    func loadViewFromNib() {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: "SimpleCustomView", bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        self.addSubview(view);



    }


}

Access customview programatically

self.customView =  SimpleCustomView(frame: CGRectMake(100, 100, 200, 200))
        self.view.addSubview(self.customView!);

Source code - https://github.com/karthikprabhuA/CustomXIBSwift

karthikPrabhu Alagu
  • 3,371
  • 1
  • 21
  • 25
9

Building on the above solutions.

This will work across all project bundles and no need for generics when calling fromNib().

Swift 2

extension UIView {

    public class func fromNib() -> Self {
        return fromNib(nil)
    }

    public class func fromNib(nibName: String?) -> Self {

        func fromNibHelper<T where T : UIView>(nibName: String?) -> T {
            let bundle = NSBundle(forClass: T.self)
            let name = nibName ?? String(T.self)
            return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
        }
        return fromNibHelper(nibName)
    }
}

Swift 3

extension UIView {

    public class func fromNib() -> Self {
        return fromNib(nibName: nil)
    }

    public class func fromNib(nibName: String?) -> Self {
        func fromNibHelper<T>(nibName: String?) -> T where T : UIView {
            let bundle = Bundle(for: T.self)
            let name = nibName ?? String(describing: T.self)
            return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
        }
        return fromNibHelper(nibName: nibName)
    }
}

Can be used like this:

let someView = SomeView.fromNib()

Or like this:

let someView = SomeView.fromNib("SomeOtherNibFileName")
Genesis
  • 8,038
  • 3
  • 21
  • 22
9

Swift 4

Don't forget to write ".first as? CustomView".

if let customView = Bundle.main.loadNibNamed("myXib", owner: self, options: nil)?.first as? CustomView {    
    self.view.addSubview(customView)
    }

If you want to use anywhere

The Best Solution is Robert Gummesson's answer.

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

Then call it like this:

let myCustomView: CustomView = UIView.fromNib()
Ali Ihsan URAL
  • 1,894
  • 1
  • 20
  • 43
7

I prefer this solution (based on the answer if @GK100):

  1. I created a XIB and a class named SomeView (used the same name for convenience and readability). I based both on a UIView.
  2. In the XIB, I changed the "File's Owner" class to SomeView (in the identity inspector).
  3. I created a UIView outlet in SomeView.swift, linking it to the top level view in the XIB file (named it "view" for convenience). I then added other outlets to other controls in the XIB file as needed.
  4. In SomeView.swift, I loaded the XIB inside the init or init:frame: CGRect initializer. There is no need to assign anything to "self". As soon as the XIB is loaded, all outlets are connected, including the top level view. The only thing missing, is to add the top view to the view hierarchy:

    class SomeView: UIView {
      override init(frame: CGRect) {
        super.init(frame: frame)
        NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil)
        self.addSubview(self.view);    // adding the top level view to the view hierarchy
      }
    
      required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil)
        self.addSubview(self.view);    // adding the top level view to the view hierarchy
      }
    
    
      ...
    }
    
Michael
  • 32,527
  • 49
  • 210
  • 370
  • i prefer to use init with frame so i uprooted this! one thing to note... add self.view.frame = frame if you'd like the view to match the frame you pass in – Michael E Jun 26 '16 at 08:37
7

Updated for Swift 5

Somewhere define below:

extension UIView {
    public class func fromNib<T: UIView>() -> T {
        let name = String(describing: Self.self);
        guard let nib = Bundle(for: Self.self).loadNibNamed(
                name, owner: nil, options: nil)
        else {
            fatalError("Missing nib-file named: \(name)")
        }
        return nib.first as! T
    }
}

And use above like:

let view: MyCustomView = .fromNib();

Which will search in same bundle as MyCustomView, then load MyCustomView.nib file (if file exists, and is added to project).

Top-Master
  • 7,611
  • 5
  • 39
  • 71
6

A nice way to do this with Swift is to use an enum.

enum Views: String {
    case view1 = "View1" // Change View1 to be the name of your nib
    case view2 = "View2" // Change View2 to be the name of another nib

    func getView() -> UIView? {
        return NSBundle.mainBundle().loadNibNamed(self.rawValue, owner: nil, options: nil).first as? UIView
    }
}

Then in your code you can simply use:

let view = Views.view1.getView()
totiDev
  • 5,189
  • 3
  • 30
  • 30
  • 2
    Note that if you do this with an empty nib file or a nib file with a none UIView root node you will crash as you are not sanity checking array size or the element in position 0. – Matthew Cawley Apr 25 '19 at 01:56
5
let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]
BaSha
  • 2,356
  • 3
  • 21
  • 38
  • But in init() of Swift, there is no returned value. I forgot to mention I need to call loadNibNamed in the initialization of an UIView. – Bagusflyer Jul 21 '14 at 05:16
  • What do you mean "no return value"? `self` is implicitly returned from all `init` methods... – Grimxn Jul 21 '14 at 08:41
  • 1
    What I mean is I call loadNibNamed inside the init method. the loaded UIView is assigned to self in ObjC. But in swift, it's not. – Bagusflyer Jul 22 '14 at 00:43
5

I just do this way :

if let myView = UINib.init(nibName: "MyView", bundle: nil).instantiate(withOwner: self)[0] as? MyView {
    // Do something with myView
}

This sample uses the first view in the nib "MyView.xib" in the main bundle. But you can vary either the index, the nib name, or the bundle ( main by default ).

I used to awake views into the view init method or make generic methods as in the proposed answers above ( which are smart by the way ), but I don't do it anymore because I have noticed use cases are often different, and to cover all cases, generic methods become as complex as using the UINib.instantiate method.

I prefer to use a factory object, usually the ViewController that will use the view, or a dedicated factory object or view extension if the view needs to be used in multiple places.

In this example, a ViewController loads a view from nib. The nib file can be changed to use different layouts for the same view class. ( This not nice code, it just illustrates the idea )

class MyViewController {
    // Use "MyView-Compact" for compact version
    var myViewNibFileName = "MyView-Standard"

    lazy var myView: MyView = {
        // Be sure the Nib is correct, or it will crash
        // We don't want to continue with a wrong view anyway, so ! is ok
        UINib.init(nibName: myViewNibFileName, bundle: nil).instantiate(withOwner: self)[0] as! MyView
    }()
}
Moose
  • 2,607
  • 24
  • 23
5

Swift 5 - Clean and easy to use extension

[Copy Paste from production project]

//
//  Refactored by Essam Mohamed Fahmi.
//

import UIKit

extension UIView
{
   static var nib: UINib
   {
      return UINib(nibName: "\(self)", bundle: nil)
   }

   static func instantiateFromNib() -> Self?
   {
      return nib.instantiate() as? Self
   }
}

extension UINib
{
   func instantiate() -> Any?
   {
      return instantiate(withOwner: nil, options: nil).first
   }
}

Usage

let myCustomView: CustomView = .instantiateFromNib()
Essam Fahmi
  • 1,920
  • 24
  • 31
4

Swift 3 version of Logan's answer

extension UIView {
    public class func fromNib(nibName: String? = nil) -> Self {
        return fromNib(nibName: nibName, type: self)
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
        return fromNib(nibName: nibName, type: T.self)!
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String

        if let nibName = nibName {
            name = nibName
        } else {
            name = self.nibName
        }

        if let nibViews = Bundle.main.loadNibNamed(name, owner: nil, options: nil) {
            for nibView in nibViews {
                if let tog = nibView as? T {
                    view = tog
                }
            }
        }

        return view
    }

    public class var nibName: String {
        return "\(self)".components(separatedBy: ".").first ?? ""
    }

    public class var nib: UINib? {
        if let _ = Bundle.main.path(forResource: nibName, ofType: "nib") {
            return UINib(nibName: nibName, bundle: nil)
        } else {
            return nil
        }
    }
}
4

Here is a clean and declarative way of programmatically loading a view using a protocol and protocol extension (Swift 4.2):

protocol XibLoadable {
    associatedtype CustomViewType
    static func loadFromXib() -> CustomViewType
}

extension XibLoadable where Self: UIView {
    static func loadFromXib() -> Self {
        let nib = UINib(nibName: "\(self)", bundle: Bundle(for: self))
        guard let customView = nib.instantiate(withOwner: self, options: nil).first as? Self else {
            // your app should crash if the xib doesn't exist
            preconditionFailure("Couldn't load xib for view: \(self)")
        }
        return customView
    }
}

And you can use this like so:

// don't forget you need a xib file too
final class MyView: UIView, XibLoadable { ... }

// and when you want to use it
let viewInstance = MyView.loadFromXib()

Some additional considerations:

  1. Make sure your custom view's xib file has the view's Custom Class set (and outlets/actions set from there), not the File Owner's.
  2. You can use this protocol/extension external to your custom view or internal. You may want to use it internally if you have some other setup work when initializing your view.
  3. Your custom view class and xib file need to have the same name.
jason z
  • 1,377
  • 13
  • 19
2

All you have to do is call init method in your UIView class.

Do it that way:

class className: UIView {

    @IBOutlet var view: UIView!

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

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

    func setup() {
        UINib(nibName: "nib", bundle: nil).instantiateWithOwner(self, options: nil)
        addSubview(view)
        view.frame = self.bounds
    }
}

Now, if you want to add this view as a sub view in view controller, do it that way in view controller.swift file:

self.view.addSubview(className())
Alap Anerao
  • 2,083
  • 22
  • 27
1

Similar to some of the answers above but a more consistent Swift3 UIView extension:

extension UIView {
    class func fromNib<A: UIView> (nibName name: String, bundle: Bundle? = nil) -> A? {
        let bundle = bundle ?? Bundle.main
        let nibViews = bundle.loadNibNamed(name, owner: self, options: nil)
        return nibViews?.first as? A
    }

    class func fromNib<T: UIView>() -> T? {
        return fromNib(nibName: String(describing: T.self), bundle: nil)
    }
}

Which gives the convenience of being able to load the class from a self named nib but also from other nibs/bundles.

1

If you want the Swift UIView subclass to be entirely self contained, and have the ability to be instantiated using init or init(frame:) without exposing the implementation detail of using a Nib, then you can use a protocol extension to achieve this. This solution avoids the nested UIView hierarchy as suggested by many of the other solutions.

public class CustomView: UIView {

    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var valueLabel: UILabel!

    public convenience init() {
        self.init(frame: CGRect.zero)
    }

    public override convenience init(frame: CGRect) {
        self.init(internal: nil)
        self.frame = frame
    }

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

    fileprivate func commonInit() {
    }
}

fileprivate protocol _CustomView {
}

extension CustomView: _CustomView {
}

fileprivate extension _CustomView {

    // Protocol extension initializer - has the ability to assign to self, unlike
    // class initializers. Note that the name of this initializer can be anything
    // you like, here we've called it init(internal:)

    init(internal: Int?) {
        self = Bundle.main.loadNibNamed("CustomView", owner:nil, options:nil)![0] as! Self;
    }
}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
appfigurate
  • 136
  • 1
  • This is an ingenious trick; the only downside is that you still can't write a fully custom init that also sets up stored properties since `init(coder:)` will be called further down the chain, which will expect said properties to already be set or to set them itself before getting back to the protocol-based init. – idrougge Oct 29 '20 at 13:02
1

You can do this via storyboard, just add proper constraints for view. You can do this easily by subclassing any view from your own let's say BaseView:

Objective-C

BaseView.h


/*!
 @class BaseView
 @discussion Base View for getting view from xibFile
 @availability ios7 and later
 */
@interface BaseView : UIView

@end


BaseView.m


#import "BaseView.h"

@implementation BaseView

#pragma mark - Public

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self prepareView];
    }
    return self;
}

#pragma mark - LifeCycle

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self prepareView];
    }
    return self;
}

#pragma mark - Private

- (void)prepareView
{
    NSArray *nibsArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
    UIView *view = [nibsArray firstObject];

    view.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:view];
    [self addConstraintsForView:view];
}

#pragma mark - Add constraints

- (void)addConstraintsForView:(UIView *)view
{
    [self addConstraints:@[[NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeBottom
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeBottom
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeTop
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeTop
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeLeft
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeLeft
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeRight
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeRight
                                                       multiplier:1.0
                                                         constant:0]
                           ]];
}

@end

Swift 4

import UIKit

class BaseView : UIView {

    // MARK: - LifeCycle

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

        prepareView()
    }

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

        prepareView()
    }

    internal class func xibName() -> String {
        return String(describing: self)
    }

    // MARK: - Private
    fileprivate func prepareView() {
        let nameForXib = BaseView.xibName()
        let nibs = Bundle.main.loadNibNamed(nameForXib, owner: self, options: nil)
        if let view = nibs?.first as? UIView {
            view.backgroundColor = UIColor.clear
            view.translatesAutoresizingMaskIntoConstraints = false
            addSubviewWithConstraints(view, offset: false)
        }
    }
}

UIView+Subview


public extension UIView {
    // MARK: - UIView+Extensions

    public func addSubviewWithConstraints(_ subview:UIView, offset:Bool = true) {
        subview.translatesAutoresizingMaskIntoConstraints = false
        let views = [
            "subview" : subview
        ]
        addSubview(subview)

        var constraints = NSLayoutConstraint.constraints(withVisualFormat: offset ? "H:|-[subview]-|" : "H:|[subview]|", options: [.alignAllLeading, .alignAllTrailing], metrics: nil, views: views)
        constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: offset ? "V:|-[subview]-|" : "V:|[subview]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views))
        NSLayoutConstraint.activate(constraints)
    }
}

I provide 2 variants how to add constraints - common one and within visual format language - select any you want :)

Also, by default assumed that xib name has same name as implementation class name. If no - just change xibName parameter.

If you subclass your view from BaseView - you can easily put any view and specify class in IB.

hbk
  • 10,908
  • 11
  • 91
  • 124
1
class func loadFromNib<T: UIView>() -> T {
    let nibName = String(describing: self)
    return Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)![0] as! T
}
Nadzeya
  • 641
  • 6
  • 16
1
    let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
    let shareView = nibs![0] as! ShareView
    self.view.addSubview(shareView)
Pratik
  • 2,399
  • 17
  • 36
1

// Use this class as super 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()
}

}

// Usages:

class HeaderView: ViewWithXib {
}


let header = HeaderView() // No need to load the view from nib, It will work
DEEPAK KUMAR
  • 341
  • 4
  • 8
1

Robert Gummesson's Answer is perfect. But when we try to use it in SPM or framework it is not working.
I've modified like below to make it work.

internal class func fromNib<T: UIView>() -> T {
    return Bundle.module.loadNibNamed(String(describing: T.self), owner: self, options: nil)![0] as! T
}
Mrugesh Tank
  • 3,495
  • 2
  • 29
  • 59
0

More powerful version based on Logan's answer

extension UIView {
    public class func fromNib(nibName: String? = nil) -> Self {
        return fromNib(nibName: nibName, type: self)
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
        return fromNib(nibName: nibName, type: T.self)!
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String

        if let nibName = nibName {
            name = nibName
        } else {
            name = self.nibName
        }

        if let nibViews = nibBundle.loadNibNamed(name, owner: nil, options: nil) {
            if nibViews.indices.contains(nibIndex), let tog = nibViews[nibIndex] as? T {
                view = tog
            }
        }

        return view
    }

    public class var nibName: String {
        return "\(self)".components(separatedBy: ".").first ?? ""
    }

    public class var nibIndex: Int {
        return 0
    }

    public class var nibBundle: Bundle {
        return Bundle.main
    }
}

And you can use like

class BaseView: UIView {
    override class var nibName: String { return "BaseView" }
    weak var delegate: StandardStateViewDelegate?
}

class ChildView: BaseView {
    override class var nibIndex: Int { return 1 }
}
user2790103
  • 315
  • 3
  • 11
0

The most convenient implementation. Here you need two methods, in order to return directly to the object of your class, not UIView.

  1. viewId marked as a class, allowing override
  2. Your .xib can contain more than one view of the top level, this situation is also handled correctly.

extension UIView {

class var viewId: String {
    return String(describing: self)
}

static func instance(from bundle: Bundle? = nil, nibName: String? = nil,
                    owner: Any? = nil, options: [AnyHashable : Any]? = nil) -> Self? {

    return instancePrivate(from: bundle ?? Bundle.main,
                           nibName: nibName ?? viewId,
                           owner: owner,
                           options: options)
}

private static func instancePrivate<T: UIView>(from bundle: Bundle, nibName: String,
                                              owner: Any?, options: [AnyHashable : Any]?) -> T? {

    guard
        let views = bundle.loadNibNamed(nibName, owner: owner, options: options),
        let view = views.first(where: { $0 is T }) as? T else { return nil }

    return view
}
}

Example:

guard let customView = CustomView.instance() else { return }

//Here customView has CustomView class type, not UIView.
print(customView is CustomView) // true
SeRG1k
  • 121
  • 2
  • 5
0
  let bundle = Bundle(for: type(of: self))
   let views = bundle.loadNibNamed("template", owner: self, options: nil)
    self.view.addSubview(views?[0] as! UIView)
Divesh singh
  • 409
  • 4
  • 12
  • Code only answers are discouraged. Please add some explanation as to how this solves the problem, or how this differs from the existing answers. [From Review](https://stackoverflow.com/review/low-quality-posts/25305520) – Nick Feb 09 '20 at 01:50
0

I prefer the below extension

extension UIView {
    class var instanceFromNib: Self {
        return Bundle(for: Self.self)
            .loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
    }
}

The difference between this and the top answered extension is you don't need to store it an constant or variable.

class TitleView: UIView { }

extension UIView {
    class var instanceFromNib: Self {
        return Bundle(for: Self.self)
            .loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
    }
}

self.navigationItem.titleView = TitleView.instanceFromNib
iCoder
  • 1,645
  • 1
  • 15
  • 23
  • What version of Xcode are you using? Please make sure you are using the latest version of XCode. Works fine for me with XCode 11.5 (latest version as on date). – iCoder Jun 01 '20 at 21:39