IBOutlet properties nil after custom view loaded from xib

25,955

Solution 1

That's expected, because the IBOutlet(s) are not assigned by the time the initializer is called. You don't need the commonInit, just an override of awakeFromNib as follows:

override func awakeFromNib() {
    super.awakeFromNib()

    print(aButt)
}

Solution 2

Assuming you tried the standard troubleshooting steps for connecting IBOutlets, try this:

Apparently, you need to disable awake from nib in certain runtime cases.

  override func awakeAfter(using aDecoder: NSCoder) -> Any? {
      guard subviews.isEmpty else { return self }
      return Bundle.main.loadNibNamed("MainNavbar", owner: nil, options: nil)?.first
  }

Solution 3

Your nib may not be connected. My solution is quite simple. Somewhere in your project (I create a class called UIViewExtension.swift), add an extension of UIView with this handy connectNibUI method.

extension UIView {
    func connectNibUI() {
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: nil).instantiate(withOwner: self, options: nil)
        let nibView = nib.first as! UIView
        nibView.translatesAutoresizingMaskIntoConstraints = false

        self.addSubview(nibView)
        //I am using SnapKit cocoapod for this method, to update constraints.  You can use NSLayoutConstraints if you prefer.
        nibView.snp.makeConstraints { (make) -> Void in
            make.edges.equalTo(self)
        }
    }
}

Now you can call this method on any view, in your init method, do this:

override init(frame: CGRect) {
    super.init(frame: frame)
    connectNibUI()
}

Solution 4

Building on @ScottyBlades, I made this subclass:

class UIViewXib: UIView {
    // I'm finding this necessary when I name a Xib-based UIView in IB. Otherwise, the IBOutlets are not loaded in awakeFromNib.
    override func awakeAfter(using aDecoder: NSCoder) -> Any? {
        guard subviews.isEmpty else { return self }
        return Bundle.main.loadNibNamed(typeName(self), owner: nil, options: nil)?.first
    }
}

func typeName(_ some: Any) -> String {
    return (some is Any.Type) ? "\(some)" : "\(type(of: some))"
}
Share:
25,955
Gralex
Author by

Gralex

Updated on November 02, 2020

Comments

  • Gralex
    Gralex over 3 years

    Something strange going on with IBOutlets. enter image description here

    In code I've try to access to this properties, but they are nil. Code:

    class CustomKeyboard: UIView {
    
        @IBOutlet var aButt: UIButton!
        @IBOutlet var oButt: UIButton!
    
        class func keyboard() -> UIView {
            let nib = UINib(nibName: "CustomKeyboard", bundle: nil)
            return nib.instantiateWithOwner(self, options: nil).first as UIView
        }
    
        override init() {
            super.init()
            commonInit()
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            commonInit()
        }
    
        required init(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            commonInit()
        }
    
        // MARK: - Private
        private func commonInit() {
            println(aButt)
            // aButt is nil
    
            aButt = self.viewWithTag(1) as UIButton
            println(aButt)
            // aButt is not nil
        }
    }