in objective c it can be done in init method by
-(id)init{
self = [[[NSBundle mainBundle] loadNibNamed:#"ViewBtnWishList" owner:0 options:nil] objectAtIndex:0];
return self;
}
but when i do this in swift
init(frame: CGRect) {
self = NSBundle.mainBundle().loadNibNamed("ViewDetailMenu", owner: 0, options: nil)[0] as? UIView
}
cannot assign to self in a method error is shown.
now my approach is to create a view, and add the view loaded from nib to it.
anyone have a better idea?
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.
This worked for me.
override func awakeAfterUsingCoder(aDecoder: NSCoder) -> AnyObject? {
if self.subviews.count == 0 {
return loadNib()
}
return self
}
private func loadNib() -> YourCustomView {
return NSBundle.mainBundle().loadNibNamed("YourCustomViewNibName", owner: nil, options: nil)[0] as YourCustomView
}
Tested in Xcode 7 beta 4 , Swift 2.0 .
The following code will assign xib to the UIView.
You can 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
that may be a solution for you:
Swift 3.x
class func instanceFromNib() -> UIView {
return UINib(nibName: "<<NibFileName>>", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}
Swift 2.x
class func instanceFromNib() -> UIView {
return UINib(nibName: "<<NibFileName>>", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as UIView
}
I think this is the easies but also the cleanest way to assign a xib to a UIView. Xcode 7.3 and swift 2.0.
import UIKit
//Create CustomView class
class CustomView: UIView {
class func instanceFromNib() -> UIView {
return UINib(nibName: "CustomView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! UIView
}
}
//Use it
let customView = CustomView.instanceFromNib() as! CustomView
The true Swift approach is the use of protocols and protocol extensions.
I use it like this: To start I create a protocol
protocol XibInitializable {
static var name: String { get }
static var bundle: Bundle? { get }
static func fromXib() -> Self
}
then I make a default implementation of this protocol use protocol extention
extension XibInitializable where Self : UIView {
static var name: String {
return String(describing: Self.self)
}
static var bundle: Bundle? {
return nil
}
static func fromXib() -> Self {
return UINib(nibName: name, bundle: bundle).instantiate(withOwner: nil, options: nil)[0] as! Self
}
}
the implementation of our protocol is now complete
In order for this protocol to work, you need the name of our xib file and the class were the same. For example, for example
finally add the protocol and make your class "final", like here.
That's it
and use
instead of adding an extension to UIView, you could define a protocol and add the implementation to a protocol extension. You can then declare that UIView conforms to the protocol.
This allows the return type to be Self instead of UIView. So the caller doesn't have to cast to the class.
Explained here:
https://stackoverflow.com/a/33424509/845027
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
}
}
Just made a UINib extension to load a view from xib and embed into a container view using constraints, using generics and strong naming (without using Strings, assuming you have the same file name for xib and implementation):
extension UINib {
static func instantiateViewAndEmbedWithConstraints <T: UIView> (viewType viewType: T.Type, embedInto containerView: UIView) -> T {
let view = UINib(nibName: String(viewType), bundle: nil).instantiateWithOwner(nil, options: nil).first as! T
containerView.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: view, attribute: .Leading, relatedBy: .Equal, toItem: containerView, attribute: .Leading, multiplier: 1, constant: 0).active = true
NSLayoutConstraint(item: view, attribute: .Trailing, relatedBy: .Equal, toItem: containerView, attribute: .Trailing, multiplier: 1, constant: 0).active = true
NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: containerView, attribute: .Top, multiplier: 1, constant: 0).active = true
NSLayoutConstraint(item: view, attribute: .Bottom, relatedBy: .Equal, toItem: containerView, attribute: .Bottom, multiplier: 1, constant: 0).active = true
return view
}
}
Usage:
...outlets...
#IBOutlet var containerView: UIView!
var customView: CustomView!
...viewDidLoad...
customView = UINib.instantiateViewAndEmbedWithConstraints(viewType: CustomView.self, embedInto: containerView)
Just subclass this simple class (swift 5):
open class NibView: UIView {
open override func awakeAfter(using coder: NSCoder) -> Any? {
if subviews.count == 0 {
return UINib(nibName: "\(Self.self)", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}
return self
}
}
class CustomView: NibView {
}
As others pointed out, set File's Owner to your CustomView class (not xib's root view itself). Then set custom class to CustomView to any view that you want to be replaced by your custom view class. Also, autolayout respects all constraints inside your xib, at least as a subview of a UITableViewCell's content view. Not sure about other cases.
As simple, as it happens to be, somehow Apple did another quest for us for such a basic thing! What a wonderful company! Never gonna be bored with them!
Related
When creating a custom view, is it possible to set the root view as the xib?
Currently if I create a custom view, I need to add the nib as a subview.
If I want to change the background color, I can no longer use the UIView APIs.
customView.backgroundColor = UIColor.red // Won't work because nib view is covering the "root" view.
So, is it possible to do this..
class CustomView: UIView {
func commonInit() {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: getInheritedClassName(object: type(of: self)), bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
// Currently doing this..
addSubview(view)
// Can we do this somehow?
self = view // Set self as the xib view
}
}
Use awakeAfter:
open override func awakeAfter(using _: NSCoder) -> Any? {
// set the tag in the view's XIB, so we don't create an infinite loop
guard tag != 999 else {
return self
}
let view = type(of: self).instantiate() // Convenience method that chooses nib based on class name
view.frame = frame
view.autoresizingMask = autoresizingMask
view.translatesAutoresizingMaskIntoConstraints = translatesAutoresizingMaskIntoConstraints
view.tag = tag
for constraint in constraints {
let firstItem = constraint.firstItem as? UIView == self ? view : constraint.firstItem
let secondItem = constraint.secondItem as? UIView == self ? view : constraint.secondItem
let constraintToAdd = NSLayoutConstraint(item: firstItem as Any, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: secondItem, attribute: constraint.secondAttribute, multiplier: constraint.multiplier, constant: constraint.constant)
constraintToAdd.priority = constraint.priority
view.addConstraint(constraintToAdd)
}
return view
}
I make Customized Tab bar Using xib I need to know how to Fetch tab bar bottom in to storyboard and I need to know how to autoresize this nib view for all views
Screenshot:
Define a UIView class named MyTabBar and use same class in place of UIView where you want to add your tab bar
class MyTabBar: NibView {
}
Now, create a superclass called NibView, which will handle everything about the XIB file merging
class NibView: UIView {
var view: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
}
extension NibView {
func xibSetup() {
view = loadNib()
view.translatesAutoresizingMaskIntoConstraints = false
view.frame = bounds
addSubview(view)
// add Constraints
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[childView]|", options: [], metrics: nil, views: ["childView": view]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[childView]|", options: [], metrics: nil, views: ["childView": view]))
}
}
extension UIView {
func loadNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nibName = type(of: self).description().components(separatedBy: ".").last!
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as! UIView
}
}
More details can be found here on how to use a nib
https://medium.com/swift2go/swift-custom-uiview-with-xib-file-211bb8bbd6eb
I have a tableview in which I am showing data coming from server. Now If there is no data coming then I show a UIView which has a label and image which show no data found . I have created a xib file for view. Now when I get view from Xib and show as tableview background view then it does not show in centre. it shows on top.
let emptyListView = EmptyListView.instanceFromNib() as! EmptyListView
emptyListView.labelNothingFound.text = AppMessages.NoDataFound.noRestaurentFound
emptyListView.center = tableView.center
tableView.backgroundView = emptyListView
tableView.separatorStyle = .none
I also tried giving custom frames but it does not work. Please tell me what is the issue ?
I faced same issue and after alot of stuggle i comeup with this logic.
Create custom view .xib file, add image and label as shown in screenshot.
And add following code in its swift file and link outlets.
import UIKit
class NoJobsViews: UIView {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var messageLabel: UILabel!
class func instanceFromNib() -> NoJobsViews {
return UINib(nibName: "NoJobsViews", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! NoJobsViews
}
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
}
Add following method in your Utility class or in extension of UIViewController
class func emptyTableViewMessageWithImage(message:String,image: String, viewController: UIViewController, tableView: UITableView) {
let noJobsView = EmptyTableViewBackgroundView.instanceFromNib()
noJobsView.imageView.image = UIImage(named: image)
noJobsView.messageLabel.text = message
tableView.backgroundView = noJobsView
tableView.separatorStyle = .none
}
Now write following lines of code in your TableView numberofRows Datasource method
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if dataSource.count == 0 {
Utility.emptyTableViewMessageWithImage(message: "No Job Found", image: kNoMyGig , viewController: self, tableView: tableView)
return 0
}
tableView.backgroundView = UIView()
return dataSource.count
}
You better make extension to UIViewController, that will make a sense so that you can use that xib where ever listing occurs.
Custom UIView class
import UIKit
class NoDataFound: UIView {
#IBOutlet var noDataLbl: UILabel!
var view = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func awakeFromNib()
{
super.awakeFromNib()
}
func setup() {
// setup the view from .xib
view = loadViewFromNib()
//view.frame = bounds
// print(view.bounds)
self.frame = view.bounds
self .isUserInteractionEnabled = true
view.backgroundColor = UIColor .black
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
}
func loadViewFromNib() -> UIView {
// grabs the appropriate bundle
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "NoDataFound", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
func showContent(msg:String){
self.noDataLbl.text = msg
}
}
Here i have created only lable in xib, u can add more UI elements, thats depends upon you.
Extension
import UIKit
extension UIViewController {
func showNoDataFound(currentView:UIView,content:String){
let noView = NoDataFound()
noView.tag = 12346
noView.translatesAutoresizingMaskIntoConstraints = false
noView.backgroundColor = UIColor.black
noView.showContent(msg: content)
currentView .addSubview(noView)
NSLayoutConstraint(item: noView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: currentView, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: noView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: currentView, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0).isActive = true
}
func removeNoviewTag(currentView:UIView){
for view in currentView .subviews{
if view.tag == 12346{
view .removeFromSuperview()
}
}
}
}
After your server response parsing
Call like below where ever you want to show no data found
if self.newsListObj.count == 0 { //your server array count
self.removeNoviewTag(currentView: self.view)
self.showNoDataFound(currentView: self.view, content: Contants.NoSavedArticleMessage)
self.favCollectionView.isHidden = true //your list table or collection view
} else {
self.removeNoviewTag(currentView: self.view)
self.favCollectionView.isHidden = false
// reload your collection or table
}
I am trying to load my XIB file into a UIView but I am having some trouble. I have the required override functions but they seem to be crashing. Saying this error, warning:
could not load any Objective-C class information. This will
significantly reduce the quality of type information available.
I was wondering if someone could show me how to properly load the XIB file into a UIView
import UIKit
class Widget: UIView {
let view = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
//call function
loadNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadNib()
//fatalError("init(coder:) has not been implemented")
}
func loadNib() {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "nib", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.addSubview(view);
}
}
I uses this in one of our projects, might be useful to you
import UIKit
class RegisterPageView: UIView {
class func instanceFromNib() -> RegisterPageView {
return UINib(nibName: "RegisterPageView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! RegisterPageView
}
}
Using Swift 3.0
let viewFromNib: UIView? = Bundle.main.loadNibNamed("NibName",
owner: nil,
options: nil)?.first
Improved DevAndArtist UIView extension
public extension UIView
{
static func loadFromXib<T>(withOwner: Any? = nil, options: [UINib.OptionsKey : Any]? = nil) -> T where T: UIView
{
let bundle = Bundle(for: self)
let nib = UINib(nibName: "\(self)", bundle: bundle)
guard let view = nib.instantiate(withOwner: withOwner, options: options).first as? T else {
fatalError("Could not load view from nib file.")
}
return view
}
}
Usage
let view = CustomView.loadFromXib()
let view = CustomView.loadFromXib(withOwner: self)
let view = CustomView.loadFromXib(withOwner: self, options: [UINibExternalObjects: objects])
External Objects discussion
Here is my approach (written in Swift 3.1):
protocol XibDesignable : class {}
extension XibDesignable where Self : UIView {
static func instantiateFromXib() -> Self {
let dynamicMetatype = Self.self
let bundle = Bundle(for: dynamicMetatype)
let nib = UINib(nibName: "\(dynamicMetatype)", bundle: bundle)
guard let view = nib.instantiate(withOwner: nil, options: nil).first as? Self else {
fatalError("Could not load view from nib file.")
}
return view
}
}
extension UIView : XibDesignable {}
Now I simply can create any UIView subclass from a Xib (assuming there is one) like so MyCustomView.instantiateFromXib(). Remember to name your Xib file exactly as your UIView subclass and set the type of the main view in that Xib file correctly.
As soon as SE-0068 will be implemented one could drop the protocol and move the function directly into the UIView extension.
Just a note: The original post uses a commonly used pattern with a nested view. IMHO this is a bad pattern which does not utilize the resources and only creates unnecessary view hierarchy.
Swift 4.x
let myView = Bundle.main.loadNibNamed("yourXibView", owner: nil, options: nil)![0] as! UIView
for swift 3
class YourClass: UIView {
class func instanceFromNib() -> YourClass {
return UINib(nibName: "YourClassNibName", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! YourClass
}
}
In my project I implemented the following (very similar to Peter's Solution)
import UIKit
// MARK: - Protocol Declaration
public protocol InterfaceBuilderInstantiable
{
/// The UINib that contains the view
///
/// Defaults to the swift class name if not implemented
static var associatedNib : UINib { get }
}
// MARK: - Default Implementation
extension InterfaceBuilderInstantiable
{
/// Creates a new instance from the associated Xib
///
/// - Returns: A new instance of this object loaded from xib
static func instantiateFromInterfaceBuilder() -> Self
{
return associatedNib.instantiate(withOwner:nil, options: nil)[0] as! Self
}
static var associatedNib : UINib
{
let name = String(describing: self)
return UINib(nibName: name, bundle: Bundle.main)
}
}
To use, you just simply implement the protocol:
class MyView: UIView, InterfaceBuilderInstantiable
{
// The rest
And if your nib is the same name as your class (MyView.xib), you're set: the default implementation of the protocol looks for a nib with the same name as the class in the main bundle.
Of course, if your nib is in another bundle or has a different name you can override the associatedNib and return your own nib.
Usually I use the following way to load a xib file owned by a custom UIView:
NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0];
Swift 4.x
This is finally how I did it
This is not in the customView itself. I put the code where the ViewController is loading the customView.
import UIKit
class StartMenuViewController: UIViewController {
#IBOutlet weak var customView: CustomView!
override func viewDidLoad() {
super.viewDidLoad()
let myView = Bundle.main.loadNibNamed("CustomView", owner: self, options: nil)![0] as! UIView
customView .addSubview(myView)
}
let xibView = NSBundle.mainBundle().loadNibNamed("NameXibView", owner: nil, options: nil)[0] as! UIView
Swift 5.x
let loadMusicView = Bundle.main.loadNibNamed("MusicView", owner: nil, options: nil)![0] as? MusicView
loadMusicView?.frame = controlsMainView.bounds
loadMusicView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
controlsMainView.addSubview(loadMusicView!)
//if you have variables in your .xib file access those variables like this
loadMusicView.yourVariableName = .....
I would like to share this piece of code that required me some effort to make it resilient.
import Foundation
protocol Nib {
func registerNib()
}
extension Nib where Self : UIView {
func registerNib() {
guard let nibName = type(of: self).description().components(separatedBy: ".").last else { return }
// ** Check if resource is used in Interface Builder first to avoid crash during compile
#if !TARGET_INTERFACE_BUILDER
let bundle = Bundle(for: type(of: self))
guard let _ = bundle.path(forResource: nibName, ofType: "nib")
else { fatalError("can't find \(nibName) xib resource in current bundle") }
#endif
guard let view = Bundle(for: type(of: self)).loadNibNamed(nibName, owner: self, options: nil)?.first as? UIView
else { return }
// ** Another way to write it but do not work if xib is bundled with framework
//guard let view = UINib(nibName: nibName, bundle: nil).instantiate(withOwner: self, options: nil).first as? UIView
// else { return }
view.frame = bounds
addSubview(view)
}
}
You can use this as follow creating a xib resource file named as class name (aka CustomView.xib)
import UIKit
class CustomView: UIView, Nib {
override init(frame: CGRect) {
super.init(frame: frame)
postInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
postInit()
}
func postInit() {
registerNib()
}
}
Do not forget to set xib resource file's owner class to CustomView and leave blank custom class class field.
//**Just use this class as super class for the 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()
}
}
// Usage
class HeaderView: ViewWithXib {
}
let header = HeaderView() // No need to load the view from nib, It will work
func configureNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "CustomUIView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
And use this tutorial for custom view with xib...
https://developerfly.com/custom-view-use-xib-swift/
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) {} ??
}
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
}
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
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
}
}
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
}
}
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))
}
}
Swift 4
Here in my case I have to pass data into that custom view, so I create static function to instantiate the view.
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
}
}
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
}
}
Set custom class to MyCustomView in .xib file. Connect outlet if necessary as usual.
Instantiate view
let view = MyCustomView.instantiate(message: "Hello World.")
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.
Also, make sure that you connect the outlet in MyClassView to the label:
To instantiate it:
let myClassView = MyClassView.createMyClassView()
myClassView.myLabel.text = "Hello World!"
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
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)
create view from .xib
let nib = UINib(nibName: "View1", bundle: nil) //View1 is a file name(View1.swift)
if let view = nib.instantiate(withOwner: self, options: nil).first as? UIView {
// logic
}
//or
if let view = Bundle.main.loadNibNamed("View1", owner: self, options: nil)?.first as? UIView {
// logic
}
Since .xib can contains several view, that is why you are working with array here(.first)
For example
Create View1.xib
Create View1.swift where set owner(loadNibNamed()) in code to create the instance of class("View1")
Set File's Owner in View1.xib as View1. Allows to connect outlets and actions
import UIKit
class View1: UIView {
#IBOutlet var contentView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.commonInit()
}
private func commonInit() {
if let view = Bundle.main.loadNibNamed("View1", owner: self, options: nil)?.first as? UIView {
addSubview(view)
view.frame = self.bounds
}
}
}
Notes
if we move Custom Class from File's owner to Container View we get error(loop). It is because of:
System init instance from Container View where we init it again in commonInit()
.loadNibNamed
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ff7bf6fbfc8)
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)
})
})
}