Custom UIView with Nib in swift without using loadFromNib in the view controller

diegomen picture diegomen · Jan 23, 2015 · Viewed 19.8k times · Source

I've always used the loadNibNamed method for loading custom views into view controllers, but now i'm trying to avoid calling that method outside the custom view for making it more reusable so that if another person uses my custom view he only will need to instantiate the view without loadFromNib, for example:

var myView: MyView = MyView()

And adding this view to the view controller's view would be enough, the custom view will load the nib inside itself. I'm trying to do it in Swift, in ObjC I've found code like the one of this answer: UIView and initWithFrame and a NIB file. How can i get the NIB file loaded? But in swift I can't use the init used in the answer:

- (id)initWithFrame:(CGRect)frame 
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code.
        //
        [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
        [self addSubview:self.view];
    }
    return self;
}

I have this method and it ends with an infinite loop:

override init(frame: CGRect) {
    super.init(frame: frame)
    self.loadFromNibNamed("MyView")
}

I've also tried adding another view inside MyView as a IBOutlet like the other answer says and using all the inits:

@IBOutlet var view: UIView!

override init() {
    super.init()
    NSBundle.mainBundle().loadNibNamed("MediaPlayerView", owner: self, options: nil)
    self.addSubview(self.view)
}

override init(frame: CGRect) {
    super.init(frame: frame)
    NSBundle.mainBundle().loadNibNamed("MediaPlayerView", owner: self, options: nil)
    self.addSubview(self.view)
}

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

But still got the error of the infinite loop.

I can't find a good solution to that and it's driving me crazy!! Somebody can help me please? Thanks!

Answer

MarkHim picture MarkHim · Jan 30, 2017

I think the cleaner approach is to not use the default initializers, but using your own e.g. like this

override init(frame: CGRect, shouldLoadFromNib: Bool) {
    super.init(frame)
    guard shouldLoadFromNib else {
         return
    }
    NSBundle.mainBundle().loadNibNamed("MediaPlayerView", owner: self, options: nil)
    self.addSubview(self.view)

}

This way, when you're instantiating it from somewhere else, you will see immediately that this is not just the plain old instantiation of some UIView but that it has side effects (that the view will be loaded from a nib)

I am not sure about the super.init(frame) call, it's possible that loadNibNamed will also call that for you, so you might want to check that out