7
votes

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!

2
Does your NIB contain a view of class MyView or MediaPlayerView, resp.? This could be the reason of the infinite loop, and I would guess init(coder:) as the cause.clemens

2 Answers

0
votes

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

0
votes

You're getting an infinite loop, because loadNibNamed(_,owner:,options:) call an init, so then try to load again the nib, and again an so on... Try this:

Swift 4

var contentView: UIView?

// MARK: Initializers

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

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

override func prepareForInterfaceBuilder() {
    super.prepareForInterfaceBuilder()
    configureView()
}

override func awakeFromNib() {
    super.awakeFromNib()
    configureView()
}

// MARK: Config Functions

func configureView() {
    guard let view = defaultView() else { return }
    view.frame = bounds
    view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    addSubview(view)
    contentView = view

    updateView()
}

func defaultView() -> UIView? {
    // Create view from a nib with the same name
    let nibName = String(describing: type(of: self))
    let bundle = Bundle(for: type(of: self))
    let nib = UINib(nibName: nibName, bundle: bundle)

    return nib.instantiate(withOwner: self, options: nil).first as? UIView
}

What this code does, is load the view from the nib, an then adding it to a subview inside your view.

This will work also when adding your view on another xibs, add @IBDesignable and it will become available