Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift proper way to load xib file

I have some strange problem with loading xib file in swift project. It's so frustrating because I already know how to do it in Obj-C. But since swift is swift so you can't do it like you did.. :/

So I have create IconTextFiled.xib and IconTextField.swift. (extends UITextField) In xib I fill field Class in Idenity inspector and in storyboard I do the same for some textFields. So we good to go just add loading from xib to init method? No.

In objc I would do it like this

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"IconTextField" owner:self options:nil];
        self = [nib objectAtIndex:0];
    }
    return self;
}

So I thought if I translate to swift it will be good.

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let nib:NSArray = NSBundle.mainBundle().loadNibNamed("IconTextField", owner: self, options: nil)
    self = nib.objectAtIndex(0)
}

But that doeasn't work. I don't know why but it try to create much more object and crash

Finally I found extension

extension IconTextField {
    class func loadFromNibNamed(nibNamed: String, bundle : NSBundle? = nil) -> IconTextField? {
        return UINib(
            nibName: nibNamed,
            bundle: bundle
            ).instantiateWithOwner(nil, options: nil)[0] as? IconTextField
    }
}

So in ViewController it looks like

@IBOutlet var password: IconTextField!
override func viewDidLoad() {
    super.viewDidLoad()
    password = IconTextField.loadFromNibNamed("IconTextField")
}

And again fail. Could you tell me how you load and use xib files?

UPDATE

Ok following after Daniel anwser

My current code

class IconTextField: UITextField {
    @IBOutlet var icon: UIImageView!
    @IBOutlet weak var view: UIView!

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        NSLog("initWithCoder \(self)")
        NSBundle.mainBundle().loadNibNamed("IconTextField", owner: self, options: nil)
        self.addSubview(view)
    }   
}

enter image description here

Those two var are connected to those views

Consoleo output, was a lot bigger and end with EXC_BAD_ACCESS

2014-10-24 10:09:09.984 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7be7bfb0;>
2014-10-24 10:09:09.984 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7be7ddf0;>
2014-10-24 10:09:09.985 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7be7fa20;>
2014-10-24 10:09:09.985 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7be814f0;>
2014-10-24 10:09:09.986 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7be830c0;>
2014-10-24 10:09:10.083 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7d183270;>
2014-10-24 10:09:10.084 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7d187cd0;>
2014-10-24 10:09:10.084 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7d189960;>

It should be only two initWithCoder. It seams that func loadNibNamed is calling initWithCoder

like image 795
Błażej Avatar asked Oct 24 '14 00:10

Błażej


2 Answers

This works for me:

class IconTextField: UITextField {
    @IBOutlet weak var view: UIView!
    @IBOutlet weak var test: UIButton!

    required init(coder: NSCoder) {
        super.init(coder: coder)
        NSBundle.mainBundle().loadNibNamed("IconTextField", owner: self, options: nil)
        self.addSubview(view)
        assert(test != nil, "the button is conected just like it's supposed to be")
    }
}

Once loadNibNamed:owner:options: is called the view and test button are connected to the outlets as expected. Adding the nib's view self's subview hierarchy makes the nib's contents visible.

like image 129
Daniel T. Avatar answered Oct 01 '22 09:10

Daniel T.


I prefer to load from nib, by implementing loadFromNib() function in a protocol extension as follows:

(as 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
  }

}
like image 20
Sam Avatar answered Oct 01 '22 08:10

Sam