2

I've created a custom view using a .xib file for the layout. I would like to programatically create and add this view as a subview to a different view.

Subview class:

class Subview: UIView {

@IBOutlet var view: UIView!
@IBOutlet var titleLabel: UILabel!
@IBOutlet var textLabel: UILabel!

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

    Bundle.main.loadNibNamed("Subview", owner: self, options: nil)
    self.addSubview(view)
    view.frame = self.bounds
  }
}

I'm not sure how to go about it, the only constructor currently expects an instance of NSCoder which I don't have.

I've tried to give it an additional initialiser:

init(title: String, text: String) {
    titleLabel.text = title
    textLabel.text = text
    super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
}

And then call it:

let v = Subview(title: dataSource[i].whatever, text: dataSource[i].bla)

But it fails when trying to unwrap the label outlets, which makes sense as the .xib hasn't been initialised.

I've already tried using a tableview and custom cells instead of programatically building the UI for the page, it doesn't really work because this is a detail page and I only have a single object as my data source instead of the array which the rowForIndexPath tableview delegate expects. I know I could write some mapper method to turn the object into an array of it's properties but I'd rather avoid that if possible.

Thanks.

mike
  • 2,073
  • 4
  • 20
  • 31
  • Because in your case only the class has been initialized not the outlets as nib initialization was not assigned, you can try `self = Bundle.main.loadNibNamed("Subview", owner: self, options: nil)?[0]` – iphonic Mar 20 '17 at 14:31
  • look ..http://stackoverflow.com/a/33946855/4003548. – vaibhav Mar 20 '17 at 14:41
  • https://stackoverflow.com/questions/35659714/loading-a-xib-file-to-a-uiview-swift/35666644#35666644 This should be helpfull – Peter Combee Mar 20 '17 at 14:48
  • Thanks for the responses, when I try either solution I'm seeing this error when I try using Bundle.main.loadLibNamed: this class is not key value coding-compliant for the key, which I think is an issue with the outlets. I should also note, I can add the xib to my storyboard with no problems at all, I may need to create more than one which is why I need to figure out how to do it programatically. – mike Mar 20 '17 at 14:51

3 Answers3

4

1) Go to Subview.xib and make sure that the file owner is left empty.

enter image description here

Also, make sure that the class for your view is selected as Subview.

enter image description here

2) Get rid of the initializer, so:

class Subview: UIView {

    @IBOutlet var view: UIView!
    @IBOutlet var titleLabel: UILabel!
    @IBOutlet var textLabel: UILabel!

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

        Bundle.main.loadNibNamed("Subview", owner: self, options: nil)
        self.addSubview(view)
        view.frame = self.bounds
    }
}

should be:

class Subview: UIView {

    @IBOutlet var view: UIView!
    @IBOutlet var titleLabel: UILabel!
    @IBOutlet var textLabel: UILabel!
}

3) Initialize you view this way:

let view = UINib(nibName: "Subview", bundle: nil).instantiate(withOwner: nil, options: nil).first as! Subview

You can also check out these.

http://supereasyapps.com/blog/2014/12/15/create-an-ibdesignable-uiview-subclass-with-code-from-an-xib-file-in-xcode-6

How to initialise a UIView Class with a xib file in Swift, iOS

Good luck!

Community
  • 1
  • 1
timaktimak
  • 1,380
  • 1
  • 12
  • 21
  • +1 but the only issue I found with this, is that the bounds aren't set this way and the custom view is not displaying correctly. – Houman Aug 16 '18 at 06:25
1

If you need to handle IBOutlets I suggest you use a UIViewController as the owner of the xib instead of a UIView (when creating a UIViewController from File menu there is the option to create also a xib)

Then you can instantiate it this way in the parent view

    var subviewController: SubviewController?

    override func viewDidLoad() {
        super.viewDidLoad()

        subviewController = SubviewController(nibName: nil, bundle: nil)

        self.view.addSubview(subviewController!.view)

        //Remember to call the IBOutlets only after adding the view
        subviewController!.titleLabel.text = "This is the title" 

        subviewController!.view.frame = self.view.frame //Not suggested
        //Instead set constraints here
    }

Either way you have to set constraints or end using viewDidLayoutSubviews() as suggested in Swift: Add a xib into a UIView

Community
  • 1
  • 1
hermannb
  • 58
  • 2
  • 6
0

Custom subclasses can be tricky, particularly when using xib files. With the code you've shown, it's not clear where you're going wrong - but yeah, probably losing the Outlet connections.

Here's a working example - includes Obj-C and Swift versions, including using the custom classes within Interface Builder. Specifically for what you're trying to do, look at the Swift3/SW3LoadFromXIB example:

https://github.com/DonMag/IBDesignInspect

DonMag
  • 69,424
  • 5
  • 50
  • 86