Load View from an External Xib File in Storyboard

Load view from an external xib file in storyboard

My full example is here, but I will provide a summary below.

Layout

Add a .swift and .xib file each with the same name to your project. The .xib file contains your custom view layout (using auto layout constraints preferably).

Make the swift file the xib file's owner.

Sample Image
Code

Add the following code to the .swift file and hook up the outlets and actions from the .xib file.

import UIKit
class ResuableCustomView: UIView {

let nibName = "ReusableCustomView"
var contentView: UIView?

@IBOutlet weak var label: UILabel!
@IBAction func buttonTap(_ sender: UIButton) {
label.text = "Hi"
}

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

guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view)
contentView = view
}

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

Use it

Use your custom view anywhere in your storyboard. Just add a UIView and set the class name to your custom class name.

Sample Image

Reuse a uiview xib in storyboard

Reuse and render a xib in a storyboard.

Tested with Swift 2.2 & Xcode 7.3.1

1 ---- Create a new UIView named 'DesignableXibView'

  • File > New > File > Source > Cocoa Touch Class > UIView

2 ---- Create a matching xib file named 'DesignableXibView'

  • File > New > File > User Interface > View

3 ---- Set the file owner of the of the xib

  1. select the xib
  2. select file's owner
  3. set custom class to 'DesignableXibView' in the Identity Inspector.

Setting a Xib's Owner to a Custom Class

  • Note: Do not set the custom class of the view on the xib. Only the File Owner!

4 ---- DesignableXibView's Implementation

//  DesignableXibView.swift

import UIKit

@IBDesignable

class DesignableXibView: UIView {

var contentView : UIView?

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

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

func xibSetup() {
contentView = loadViewFromNib()

// use bounds not frame or it'll be offset
contentView!.frame = bounds

// Make the view stretch with containing view
contentView!.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]

// Adding custom subview on top of our view (over any custom drawing > see note below)
addSubview(contentView!)
}

func loadViewFromNib() -> UIView! {

let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView

return view
}

}

5 ---- Test your reuseable view in a storyboard

  1. Open your storyboard
  2. Add a view
  3. Set that view's Custom Class
  4. wait a sec ... BOOM!!

Xib Rendering Inside a Storyboard View Controller

Be able to load xib from both Storyboard and ViewController

To preserve both cases, I preferred to write this inside my subclass declaration:

@IBDesignable class CustomView: UIView {

var view: UIView!

@IBOutlet weak var button: UIButton!
@IBOutlet weak var label: UILabel!


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

func loadViewFromNib() -> UIView {

let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "CustomView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView

return view
}


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

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

In this way I can see and add it inside my storyboard.
In ViewForHeaderInSection method, I wrote this:

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

let header = UIView()

let view:CustomView = CustomView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 30))

view.label.text = "header title: \(section)"

header.addSubview(view)


return header

}

And so it works ;)



Related Topics



Leave a reply



Submit