How to initialize/instantiate a custom UIView class with a XIB file in Swift

IosSwift

Ios Problem Overview


I have a class called MyClass which is a subclass of UIView, that I want to initialize with a XIB file. I am not sure how to initialize this class with the xib file called View.xib

class MyClass: UIView {

    // what should I do here? 
    //init(coder aDecoder: NSCoder) {} ?? 
}

Ios Solutions


Solution 1 - Ios

I tested this code and it works great:

class MyClass: UIView {        
    class func instanceFromNib() -> UIView {
        return UINib(nibName: "nib file name", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as UIView
    }    
}

Initialise the view and use it like below:

var view = MyClass.instanceFromNib()
self.view.addSubview(view)

OR

var view = MyClass.instanceFromNib
self.view.addSubview(view())

UPDATE Swift >=3.x & Swift >=4.x

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

Solution 2 - Ios

Sam's solution is already great, despite it doesn't take different bundles into account (NSBundle:forClass comes to the rescue) and requires manual loading, a.k.a typing code.

If you want full support for your Xib Outlets, different Bundles (use in frameworks!) and get a nice preview in Storyboard try this:

// NibLoadingView.swift
import UIKit

/* Usage: 
- Subclass your UIView from NibLoadView to automatically load an Xib with the same name as your class
- Set the class name to File's Owner in the Xib file
*/

@IBDesignable
class NibLoadingView: UIView {

    @IBOutlet weak var view: UIView!

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

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

    private func nibSetup() {
        backgroundColor = .clearColor()

        view = loadViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        view.translatesAutoresizingMaskIntoConstraints = true

        addSubview(view)
    }

    private func loadViewFromNib() -> UIView {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
        let nibView = nib.instantiateWithOwner(self, options: nil).first as! UIView

        return nibView
    }

}

Use your xib as usual, i.e. connect Outlets to File Owner and set File Owner class to your own class.

Usage: Just subclass your own View class from NibLoadingView & Set the class name to File's Owner in the Xib file

No additional code required anymore.

Credits where credit's due: Forked this with minor changes from DenHeadless on GH. My Gist: https://gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8

Solution 3 - Ios

As of Swift 2.0, you can add a protocol extension. In my opinion, this is a better approach because the return type is Self rather than UIView, so the caller doesn't need to cast to the view class.

import UIKit

protocol UIViewLoading {}
extension UIView : UIViewLoading {}

extension UIViewLoading where Self : UIView {

  // note that this method returns an instance of type `Self`, rather than UIView
  static func loadFromNib() -> Self {
    let nibName = "\(self)".characters.split{$0 == "."}.map(String.init).last!
    let nib = UINib(nibName: nibName, bundle: nil)
    return nib.instantiateWithOwner(self, options: nil).first as! Self
  }

}

Solution 4 - Ios

And this is the answer of Frederik on Swift 3.0

/*
 Usage:
 - make your CustomeView class and inherit from this one
 - in your Xib file make the file owner is your CustomeView class
 - *Important* the root view in your Xib file must be of type UIView
 - link all outlets to the file owner
 */
@IBDesignable
class NibLoadingView: UIView {

    @IBOutlet weak var view: UIView!

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

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

    private func nibSetup() {
        backgroundColor = .clear
    
        view = loadViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.translatesAutoresizingMaskIntoConstraints = true
    
        addSubview(view)
    }

    private func loadViewFromNib() -> UIView {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        let nibView = nib.instantiate(withOwner: self, options: nil).first as! UIView
    
        return nibView
    }
}

Solution 5 - Ios

Universal way of loading view from xib:

Example:

let myView = Bundle.loadView(fromNib: "MyView", withType: MyView.self)

Implementation:

extension Bundle {
    
    static func loadView<T>(fromNib name: String, withType type: T.Type) -> T {
        if let view = Bundle.main.loadNibNamed(name, owner: nil, options: nil)?.first as? T {
            return view
        }
        
        fatalError("Could not load view with type " + String(describing: type))
    }
}

Solution 6 - Ios

Swift 4

Here in my case I have to pass data into that custom view, so I create static function to instantiate the view.

  1. Create UIView extension

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

     class MyCustomView: UIView {
    
         @IBOutlet weak var messageLabel: UILabel!
    
         static func instantiate(message: String) -> MyCustomView {
             let view: MyCustomView = initFromNib()
             view.messageLabel.text = message
             return view
         }
     }
    
  3. Set custom class to MyCustomView in .xib file. Connect outlet if necessary as usual. enter image description here

  1. Instantiate view

     let view = MyCustomView.instantiate(message: "Hello World.")
    

Solution 7 - Ios

Swift 3 Answer: In my case, I wanted to have an outlet in my custom class that I could modify:

class MyClassView: UIView {
    @IBOutlet weak var myLabel: UILabel!
    
    class func createMyClassView() -> MyClassView {
        let myClassNib = UINib(nibName: "MyClass", bundle: nil)
        return myClassNib.instantiate(withOwner: nil, options: nil)[0] as! MyClassView
    }
}

When in the .xib file, make sure that the Custom Class field is MyClassView. Don't bother with the File's Owner.

Make sure Custom Class is MyClassView

Also, make sure that you connect the outlet in MyClassView to the label: Outlet for myLabel

To instantiate it:

let myClassView = MyClassView.createMyClassView()
myClassView.myLabel.text = "Hello World!"

Solution 8 - Ios

Swift 5.3

Create a class named NibLoadingView with the following contents:

import UIKit

/* Usage:
- Subclass your UIView from NibLoadView to automatically load an Xib with the same name as your class
- Set the class name to File's Owner in the Xib file
*/

@IBDesignable
class NibLoadingView: UIView {

    @IBOutlet public weak var view: UIView!
	
	private var didLoad: Bool = false

	public override init(frame: CGRect) {
		super.init(frame: frame)
		
		self.nibSetup()
	}

	required public init?(coder aDecoder: NSCoder) {
		super.init(coder: aDecoder)
		
		self.nibSetup()
	}
	
	open override func layoutSubviews() {
		super.layoutSubviews()
		
		if !self.didLoad {
			self.didLoad = true
			self.viewDidLoad()
		}
	}
	
	open func viewDidLoad() {
		self.setupUI()
	}

	private func nibSetup() {
		self.view = self.loadViewFromNib()
		self.view.frame = bounds
		self.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
		self.view.translatesAutoresizingMaskIntoConstraints = true

		addSubview(self.view)
	}

	private func loadViewFromNib() -> UIView {
		guard let nibName = type(of: self).description().components(separatedBy: ".").last else {
			fatalError("Bad nib name")
		}
		
		if let defaultBundleView = UINib(nibName: nibName, bundle: Bundle(for: type(of: self))).instantiate(withOwner: self, options: nil).first as? UIView {
			return defaultBundleView
		} else {
			fatalError("Cannot load view from bundle")
		}
	}
}

Now create a XIB & UIView class pair, set XIB's owner to UIView class and subclass NibLoadingView.

You can now init the class just like ExampleView(), ExampleView(frame: CGRect), etc or directly from storyboards.

You can also use viewDidLoad just like in UIViewController. All your outlets and layouts are rendered in that moment.

Based on Frederik's answer

Solution 9 - Ios

Below code will do the job if anyone wants to load a custom View with XIB Programmatically.

let customView = UINib(nibName:"CustomView",bundle:.main).instantiate(withOwner: nil, options: nil).first as! UIView
customView.frame = self.view.bounds
self.view.addSubview(customView)

Solution 10 - Ios

override func draw(_ rect: CGRect) 
{
    AlertView.layer.cornerRadius = 4
    AlertView.clipsToBounds = true
   
    btnOk.layer.cornerRadius = 4
    btnOk.clipsToBounds = true   
}

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

@IBAction func okBtnDidClicked(_ sender: Any) {
    
    removeAlertViewFromWindow()
    
    UIView.animate(withDuration: 0.4, delay: 0.0, options: .allowAnimatedContent, animations: {() -> Void in
        self.AlertView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
        
    }, completion: {(finished: Bool) -> Void in
        self.AlertView.transform = CGAffineTransform.identity
        self.AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
        self.AlertView.isHidden = true
        self.AlertView.alpha = 0.0
        
        self.alpha = 0.5
    })
}


func removeAlertViewFromWindow()
{
    for subview  in (appDel.window?.subviews)! {
        if subview.tag == 500500{
            subview.removeFromSuperview()
        }
    }
}


public func openAlertView(title:String , string : String ){

    lblTital.text  = title
    txtView.text  = string
    
    self.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
    appDel.window!.addSubview(self)
    
    
    AlertView.alpha = 1.0
    AlertView.isHidden = false
   
    UIView.animate(withDuration: 0.2, animations: {() -> Void in
        self.alpha = 1.0
    })
    AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
    
    UIView.animate(withDuration: 0.3, delay: 0.2, options: .allowAnimatedContent, animations: {() -> Void in
        self.AlertView.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
       
    }, completion: {(finished: Bool) -> Void in
        UIView.animate(withDuration: 0.2, animations: {() -> Void in
            self.AlertView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
           
        })
    })


}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionStephen FoxView Question on Stackoverflow
Solution 1 - IosEzimetView Answer on Stackoverflow
Solution 2 - IosFrederik WinkelsdorfView Answer on Stackoverflow
Solution 3 - IosSamView Answer on Stackoverflow
Solution 4 - IosMahmood SView Answer on Stackoverflow
Solution 5 - IosМаксим МартыновView Answer on Stackoverflow
Solution 6 - IosaxumnemonicView Answer on Stackoverflow
Solution 7 - IosJeremyView Answer on Stackoverflow
Solution 8 - IosRadu UrsacheView Answer on Stackoverflow
Solution 9 - IosPradeep Reddy KypaView Answer on Stackoverflow
Solution 10 - IosMadan KumawatView Answer on Stackoverflow