How to Initialize/Instantiate a Custom Uiview Class with a Xib File in Swift

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

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
}

Assign xib to the UIView in Swift

for Swift 4

extension UIView {
class func loadFromNibNamed(nibNamed: String, bundle: Bundle? = nil) -> UIView? {
return UINib(
nibName: nibNamed,
bundle: bundle
).instantiate(withOwner: nil, options: nil)[0] as? UIView
}
}

for Swift 3

You could create an extension on UIView:

extension UIView {
class func loadFromNibNamed(nibNamed: String, bundle: NSBundle? = nil) -> UIView? {
return UINib(
nibName: nibNamed,
bundle: bundle
).instantiateWithOwner(nil, options: nil)[0] as? UIView
}
}

Note: Using UINib is faster because it does caching for you.

Then you can just do:

ViewDetailItem.loadFromNibNamed("ViewBtnWishList")

And you will be able to reuse that method on any view.

iOS - How to initialize custom UIView with specific Frame from NIB

You can have a NibLoading class like:

// NibLoadingView.swift
//source:https://gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8
import UIKit

// Usage: Subclass your UIView from NibLoadView to automatically load a xib with the same name as your class

@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
nibView.anchorAllEdgesToSuperview()
return nibView
}

}

extension UIView {
func anchorAllEdgesToSuperview() {
self.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 9.0, *) {
addSuperviewConstraint(constraint: topAnchor.constraint(equalTo: (superview?.topAnchor)!))
addSuperviewConstraint(constraint: leftAnchor.constraint(equalTo: (superview?.leftAnchor)!))
addSuperviewConstraint(constraint: bottomAnchor.constraint(equalTo: (superview?.bottomAnchor)!))
addSuperviewConstraint(constraint: rightAnchor.constraint(equalTo: (superview?.rightAnchor)!))
}
else {
for attribute : NSLayoutAttribute in [.left, .top, .right, .bottom] {
anchorToSuperview(attribute: attribute)
}
}
}

func anchorToSuperview(attribute: NSLayoutAttribute) {
addSuperviewConstraint(constraint: NSLayoutConstraint(item: self, attribute: attribute, relatedBy: .equal, toItem: superview, attribute: attribute, multiplier: 1.0, constant: 0.0))
}

func addSuperviewConstraint(constraint: NSLayoutConstraint) {
superview?.addConstraint(constraint)
}
}

Then your view will subclass the NibLoadingClass like:

class YourUIView: NibLoadingView {
override init(frame: CGRect) {
super.init(frame: frame)
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Set your XIB class in File's Owner like:
In this case it will be YourUIView

Sample Image

Then instantiate it:

let myView = YourUIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width-60, height: 170))

Can I use UIView (.xib and class) as view base example for many view?

Yes you can.

Once you've created the XIB, create a UIView class, assign that class to the XIB's file owner. Create an IBOutlet of the whole XIB container view attach it to the UIView class, call it something like containerView

On your XIB's custom class add the following:

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

customInit()

}

func customInit() {

Bundle.main.loadNibNamed("YourNIBName", owner: self, options: nil)
addSubview(containerView)
containerView.autoresizingMask = [.flexibleHeigh, .flexibleWidth]


}

On any of your ViewControllers now :

let view = CustomNib(frame: CGRect(x: 0, y:0, width: 150, height: 150)

self.view.addSubview(view)

You can also create this via the storyboard simply assign the custom xib class to any UIView.

Here is a great resource that can also help: Swift 3 — Creating a custom view from a xib

How to pass in extra arguments to a custom UIView that is linked to a XIB file?

I am getting errors saying Property 'self.movieCard' not initialized at super.init call for both override init and required init methods.

The Swift compiler will prevent you from creating objects that aren't fully initialized, so before you initialize the superclass you need to provide values for your class's properties.

How do I rectify this?

You need to either provide a value for your movieCard property in your initializers before you call super.init, or make movieCard optional so that the compiler will let you leave it unset until later. For example, you could change the declaration to:

var movieCard = MovieCard()

to just give it an empty MovieCard instance, and make it var so that you can change it later on. You could also mark the variable lazy so that the empty MovieCard isn't actually created unless you try to access the value of the property:

lazy var movieCard = MovieCard()

Custom UIView subclass with XIB in Swift

I was able to work it around but the solution is a little bit tricky. It's up to debate if the gain is worth an effort but here is how I implemented it purely in interface builder

First I defined a custom UIView subclass named P2View

@IBDesignable class P2View: UIView
{
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var iconView: UIImageView!

@IBInspectable var title: String? {
didSet {
if titleLabel != nil {
titleLabel.text = title
}
}
}

@IBInspectable var image: UIImage? {
didSet {
if iconView != nil {
iconView.image = image
}
}
}

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

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

override func awakeFromNib()
{
super.awakeFromNib()

let bundle = Bundle(for: type(of: self))

guard let view = bundle.loadNibNamed("P2View", owner: self, options: nil)?.first as? UIView else {
return
}

view.translatesAutoresizingMaskIntoConstraints = false
addSubview(view)

let bindings = ["view": view]

let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat:"V:|-0-[view]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: bindings)
let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat:"H:|-0-[view]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: bindings)

addConstraints(verticalConstraints)
addConstraints(horizontalConstraints)
}

titleLabel.text = title
iconView.image = image
}

This is how it looks like in interface builder

Xib definition

This is how I embedded this custom view in the example view controller defined on a storyboard. Properties of P2View are set in the attributes inspector.

Custom view embedded in the view controller

There are 3 points worth mentioning

First:

Use the Bundle(for: type(of: self)) when loading the nib. This is because the interface builder renders the designables in the separate process which main bundle is not the same as your main bundle.

Second:

    @IBInspectable var title: String? {
didSet {
if titleLabel != nil {
titleLabel.text = title
}
}
}

When combining IBInspectables with IBOutlets you have to remember that the didSet functions are called before awakeFromNib method. Because of that, the outlets are not initialized and your app will probably crash at this point. Unfortunatelly you cannot omit the didSet function because the interface builder won't render your custom view so we have to leave this dirty if here.

Third:

    titleLabel.text = title
iconView.image = image

We have to somehow initialize our controls. We were not able to do it when didSet function was called so we have to use the value stored in the IBInspectable properties and initialize them at the end of the awakeFromNib method.

This is how you can implement a custom view on a Xib, embed it on a storyboard, configure it on a storyboard, have it rendered and have a non-crashing app. It requires a hack, but it's possible.

programmatically customize UIView in xib

awakeFromNib will be called only if call UINib like this: https://stackoverflow.com/a/25513605/846780



Related Topics



Leave a reply



Submit