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
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
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.
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
Iphone: How to Get Current Milliseconds
How to Rotate Text for Uibutton and Uilabel in Objective-C
Update Restkit 'Lcl_Rk.H' File Not Found in Rklog.H
How to Get Audio Volume Level, and Volume Changed Notifications on iOS
How to Create Managedobjectcontext Using Swift 3 in Xcode 8
Cannot Change Search Bar Background Color
How to Convert My Device Token (Nsdata) into an Nsstring
Get Image from Documents Directory Swift
Record and Play Audio Simultaneously
How to Hide Keyboard in Swift on Pressing Return Key
iOS 8 Sdk: Modal Uiwebview and Camera/Image Picker
How to Insert the Uitextview into Uialertview in iOS
Chain Multiple Alamofire Requests
One Step Affine Transform for Rotation Around a Point
Why Storyboard UI Elements Not Showing on Uiviewcontroller in Xcode 6