Reputation: 439
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);
}
}
Upvotes: 29
Views: 78252
Reputation: 361
//**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
Upvotes: 1
Reputation: 17932
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 = .....
Upvotes: 1
Reputation: 595
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
}
}
Upvotes: 49
Reputation: 1005
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])
Upvotes: 18
Reputation: 502
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.
Upvotes: 0
Reputation: 817
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)
}
Upvotes: 2
Reputation: 5159
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.
Upvotes: 14
Reputation: 179
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/
Upvotes: -1
Reputation: 1187
Swift 4.x
let myView = Bundle.main.loadNibNamed("yourXibView", owner: nil, options: nil)![0] as! UIView
Upvotes: 10
Reputation: 8571
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.
Upvotes: 4
Reputation: 2215
for swift 3
class YourClass: UIView {
class func instanceFromNib() -> YourClass {
return UINib(nibName: "YourClassNibName", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! YourClass
}
}
Upvotes: 7
Reputation: 17323
Using Swift 3.0
let viewFromNib: UIView? = Bundle.main.loadNibNamed("NibName",
owner: nil,
options: nil)?.first
Upvotes: 23
Reputation: 251
let xibView = NSBundle.mainBundle().loadNibNamed("NameXibView", owner: nil, options: nil)[0] as! UIView
Upvotes: 1
Reputation: 7637
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];
Upvotes: 3