Frame size of UIView when loaded from Nib? - ios

I am loading a UIView from a .xib file like this:
static func loadFromNib() -> CardView {
let nib = UINib(nibName: "CardView", bundle: nil)
return nib.instantiate(withOwner: self, options: nil).first as! CardView
}
When loaded the view has the exact frame size as set in "Frame Rectangle" of the Size Inspector in the Interface Builder.
Is this guaranteed? I need this size to be exact, because subview constraints are specific and will not fit if the view has the wrong size [*], but I didn't find any mention of this in Apple's docs.
[*] = Reason: I am rendering the view to an UIImage so I can display it in and UIImageView later on. It shows the image of a membership card and name and membership number need to be in the right place with the correct font size on all devices..

Set any UIView subclass as owner of xib, then load xib as subview of this view and set autoresizing mask.
This is how I use it:
extension UIView {
func loadXibView(with xibFrame: CGRect) -> UIView {
let className = String(describing: type(of: self))
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: className, bundle: bundle)
guard let xibView = nib.instantiate(withOwner: self, options: nil)[0] as? UIView else {
return UIView()
}
xibView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
xibView.frame = xibFrame
return xibView
}
}
xibView.autoresizingMask = [.flexibleWidth, .flexibleHeight] sets size of view properly.
Then just use it any UIView subclass in initialization:
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(loadXibView(with: bounds))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
addSubview(loadXibView(with: bounds))
}

Create a custom class for your UIView:
class CardView: UIView {
override init(frame: CGrect) {
super.init(frame: frame)
let xibView = UINib(nibName: "CardView", bundle: nil).instantiate(withOwner: nil, options:nil)[0] as! UIView
self.addSubview(xibView)
}
require init?(coder: aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
and then call if from the class you'll be implementing it in with the frame size you need or otherwise it will default to the size that is set in your interface builder:
// MyViewController
var cardView: CardView?
override func viewDidLoad() {
super.viewDidLoad()
self.cardView = CardView()
self.cardView.frame.size = CGSize(size here)
self.cardView.frame.origin = CGPoint(point here)
self.view.addSubview(self.cardView!)
}

Related

Customized Tab bar Using xib

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

Custom UIButton - IBAction not working

I have a default UIButton linked to an IBAction (touch up inside). This works fine, so I know my connection is OK. Once I change the UIButton class to my custom button, the IBAction is no longer triggered. Below is the code for my custom UIButton. Any ideas why this is happening?
import UIKit
#IBDesignable
class PrimaryButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
initView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initView()
}
private func initView() {
let view = viewFromNibForClass()
view.frame = bounds
view.autoresizingMask = [
UIViewAutoresizing.flexibleWidth,
UIViewAutoresizing.flexibleHeight
]
addSubview(view)
}
private func viewFromNibForClass() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
return view
}
}
Your code does not work because a button does not receive touches anymore. All touches are sent to the topmost custom view you add.
Set view.userInteractionEnabled = false to make it transparent to touches.
Your custom button is covered by the view you added.

Could not load NIB in bundle Xcode8

I've followed a tutorial but when I try to run my code it crashed because it couldn't load NIB in bundle.
Here's my code:
class BuyPremiumView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
// MARK: - Private Helper Methods
// Performs the initial setup.
fileprivate func setupView() {
let view = viewFromNibForClass()
view.frame = bounds
view.autoresizingMask = [
UIViewAutoresizing.flexibleWidth,
UIViewAutoresizing.flexibleHeight
]
addSubview(view)
}
// Loads a XIB file into a view and returns this view.
fileprivate func viewFromNibForClass() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
}
Does anybody know how to fix this?
if you would load view from nib i think best easy way is :
var yourView : YourView = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as! YourView
i hope this help

Addsubview custom xib file error

I'm using Swift 3, I have error adding xib file as subview
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
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(view);
Can someone help me please?
In my Custom View Class i implemented as follow:
import UIKit
#IBDesignable class TestView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func loadViewFromNib(){
let view = Bundle.main.loadNibNamed("test", owner: self, options: nil)?.first as! UIView
print(view.backgroundColor ?? UIColor.blue)
}
}
then In my ViewController where i want add this custom view i write as below:
let view = TestView()
and i dont find any crash.
Try this, it is for swift 3.
let view = Bundle.main.loadNibNamed("CustomView", owner: self, options: nil)?.first as! UIView
self .addSubview(view)

Loading a XIB file to a UIView Swift

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/

Resources