Search code examples
iosswiftuiviewxibinit

iOS - How to initialize custom UIView with specific Frame from NIB


I am wondering what is the cleanest way to initialize a custom UIView with a specific frame.

The UIView is designed from a XIB file.

Here is my implementation :

class CustomView : UIView {

    @IBOutlet var outletLabel: UILabel!

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

    public override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    private func setupView() {

        // Set text for labels
    }
}

Here is how I want to initialize it in my ViewController :

let screenSize: CGRect = UIScreen.main.bounds
let screenWidth = screenSize.width
let frame = CGRect(x: 0, y: 0, width: screenWidth - 50, height: 70)

let customView = CustomView.init(frame: frame)

But it is not working, I have a white UIView without any outlets.

And if I do this instead :

// Extension to UIView to load Nib
let customView : CustomView = UIView.fromNib()

I can see my view from XIB file, with its width/height used in the Interface Builder.

What is I want to load the view from XIB file BUT with specific frame ?

Am I missing something about initialization ?


Solution

  • 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

    enter image description here

    Then instantiate it:

    let myView = YourUIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width-60, height: 170))