Reputation: 2193
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.
Upvotes: 2
Views: 14007
Reputation: 1380
1) Go to Subview.xib
and make sure that the file owner is left empty.
Also, make sure that the class for your view is selected as Subview
.
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.
How to initialise a UIView Class with a xib file in Swift, iOS
Good luck!
Upvotes: 4
Reputation: 77477
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
Upvotes: 0
Reputation: 58
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
Upvotes: 1