Adding a Table View Cell to a Table View within a View - ios

I have a subclass of UIView (MyView) that I've hooked up to a NIB file.
class MyView: UIView {
#IBOutlet var contentView: UIView!
#IBOutlet weak var tableView: UITableView!
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
let nibName = "MyView"
private func setup() {
let bundle = Bundle.init(for: type(of: self))
bundle.loadNibNamed(nibName, owner: self, options: nil)
contentView.frame = self.bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
self.addSubview(contentView)
}
}
This view is simple; containing only one UITableView (or tableView). However, I want to add a UITableViewCell with a UILabel to tableView but the storyboard is not letting me do this.
I understand that MyView is not a view controller and therefore should not (if following an MVC pattern) implement the various table view data source / delate methods, but, still, why can I not add this table view cell to the table view within this custom view?
My aim was to then have some UIViewController subclass that has an instance of MyView, i.e.
var myView = MyView(),
which it then controls the datasource and delegate methods for, i.e.
myView.tableView.dataSource = self.
Finally, I've attached a screenshot showing that I am unable to add this table view cell to the table view.

You cannot add prototype cells to table views in xib files - only in storyboards.
You can create your table view in one xib, and your table view cell in another xib, if you want.
Or, you can create a second storyboard that would contain a table view and it would support cell prototypes.

Related

How to allow subclasses to use parent class's nib

Say I have a parent view class, that contains at least 1 property:
class BaseView : UIView {
#IBOutlet weak var myLabel: UILabel!
}
This class has a corresponding xib file with an outlet connection made from the xib to the myLabel property.
Now let's say we also have some child classes that inherit from this class:
class ChildView : BaseView {
func setup() {}
}
ChildView has some custom logic but can reuse all of the views from BaseView. It doesn't (or I'd prefer to avoid it having) its own corresponding xib file.
I'd like to be able to do something like this:
let childView = Bundle.main.loadNibNamed(String(describing: BaseView.self), owner: nil, options:nil)?.first as! ChildViewA
but this doesn't work. Neither does:
let childView = ChildView()
Bundle.main.loadNibNamed(String(describing: BaseView.self owner: childView, options: nil)
Is there anyway to allow a child view to inherit from its parent view's xib file in a similar way?
The problem is that the root view in the nib is of type BaseView, so as! ChildViewA fails. Since you don't have access to the NSKeyedUnarchiver that the nib loader uses to unarchive the xib, there is no easy way to substitute your own class during unarchiving.
Here's a workaround.
Do not embed the BaseView itself in the xib. Instead, make the top-level view in the xib be a plain UIView, and set the File's Owner custom class to BaseView. Then delete all of the connections to the top-level view and set them on the File's Owner instead. Also give BaseView a rootViewFromNib outlet, and connect it to the root view.
Then, give BaseView an initializer that loads its nib and adds that rootViewFromNib to itself as a subview, with its frame pinned to the BaseView's own bounds. You can use autoresizing to do it.
In the end, BaseView should look like this:
class BaseView: UIView {
#IBOutlet var myLabel: UILabel!
// other outlets, etc.
#IBOutlet private var rootViewFromNib: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
Bundle(for: BaseView.self).loadNibNamed("BaseView", owner: self, options: nil)
rootViewFromNib.frame = bounds
rootViewFromNib.autoresizingMask = [.flexibleWidth, .flexibleHeight]
rootViewFromNib.translatesAutoresizingMaskIntoConstraints = true
addSubview(rootViewFromNib)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
and BaseView.xib should look like this:

Embed Custom UITableViewCell within another custom UIView?

I have a UITableViewController with a list of custom UITableViewCells. The cell has text, which is sometimes long. I am truncating that at 2 lines in the table.
When a user touches the cell, I want to create a UIView as a popup with the exact same information as the UITableViewCell, with the addition of a header above the UITableViewCell content. So, essentially, I want something like the following:
I have built VersePopupView just as indicated in the picture, where the text "Custom Header" is actually a UILabel. I have instantiated the view from the XIB file and successfully set the UILabel text, but the custom UITableViewCell doesn't show, even though I have assigned that value as well. My IBOutlets are all hooked up to IB.
Here is my instantiation:
let popupView: VersePopupView = VersePopupView.instanceFromNib()
popupView.frame = CGRect(x: 10, y: 100, width: 300, height: 100)
popupView.assignVerseTableViewCell(cell: cell)
window.addSubview(popupView)
window.makeKeyAndVisible()
And here is my VersePopupView class:
class VersePopupView: UIView {
#IBOutlet weak var cell: VerseTableViewCell!
#IBOutlet weak var title: UILabel!
#IBOutlet weak var headerView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
class func instanceFromNib() -> VersePopupView {
return UINib(nibName: "VersePopupView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! VersePopupView
}
func assignVerseTableViewCell(cell: VerseTableViewCell)
{
// Assign it
self.cell = cell
// Get the verse details
if let verseDetails = cell.verse_details {
self.title.text = verseDetails.verseReference()
// Adjust the frame
// Adjust the text to be attributed text
// Set the title to the verse reference
// Anything else?
}
}
}
What am I doing wrong such that the UITableViewCell isn't showing up?
Is there a better pattern for this rather than embedding the UITableViewCell?
I don't want to duplicate code (which I have done in the past), because I want the user to interact with the popup just like they would in the table cell.
I'm not sure how to convince you of how easy this is to do without trying to misuse a table view cell, so here's a screen shot:
You just have to believe me when I say that that's a three-row table followed by a totally independent ordinary view plucked from the inside of the table view cell (by loading the cell nib).
They not only look identical, they act identical; the view is a custom UIView subclass with an action method from the switch, and when I click the switch, it triggers that action method, both within the table and in the view outside it.
The view was designed entirely in the nib, and absolutely no code was repeated.
That seems to be the sort of thing you want to do. So, I encourage you, don't misuse table view cells; do this with an ordinary view that can live happily inside a cell or outside it.

UITableViewCell with a common element

I have a scenario where in I need to create around 10 different prototypes of UITableViewCells. How ever all of these cells have some UI Elements (area marked in black) in common. And there is an area (marked in yellow) which is different for all these prototypes.
Is there a way I can abstract all the common UI Elements like the way it is done for contentView in UITableViewCell?
I tried to create a TableViewCell with all these elements and empty UIView to hold the customizations and planned to programatically load UIView (created in separate xib) into it.
The problem is I am not able to load the UIView into the UITableViewCell without loosing the constraints?
How to load a custom view with constraints into another?
Or Is there a way to create a custom UITableViewCell like the one in IB?
Create LoadableFromXibView subclass
Create a xib file, set the File's owner as your subclass
Drag outlets and design your view as usual
In your cell insert a subview that will be your reusable part of the cell and set its name as the new LoadableFromXibView subclass that you have created.
Source here: https://gist.github.com/DenHeadless/c3d682e7f499113109d6
class LoadableFromXibView: UIView {
var view = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
func xibSetup() {
view = loadViewFromXib()
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
backgroundColor = .clearColor()
addSubview(view)
}
private func loadViewFromXib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil).first as! UIView
return view
}
}

XCode: Is it possible to create reusable stack views?

Is it possible to create reusable stack views on the story board that can be used dynamically to be generated at a later time? Sort of a template/widget/component.
I am aware that I can do this with a class but if I am able to visually generate a set of components that can be re-used at a latter time I may be able to let our designers make changes to storyboards directly.
Yes -- you can do this with any UIView. There are many tutorials for this (e.g. http://onedayitwillmake.com/blog/2013/07/ios-creating-reusable-uiviews-with-storyboard/)
The basic idea is to drag one onto Storyboard or XIB, make a custom class for it, then implement the view's awakeFromNib to load it.
Yes.It is.
Create a empty xib and then add a stack view to it.
Then create a class which extends UIStackView.
class stackView: UIStackView {
var contentView : UIStackView!
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init(coder: NSCoder) {
super.init(coder: coder)
xibSetup() }
func xibSetup() {
contentView = loadViewFromNib()
contentView.frame = bounds
contentView.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(contentView)
}
func loadViewFromNib() -> UIStackView! {
let view: UIStackView? = Bundle.main.loadNibNamed("stackView", owner: nil, options: nil)?.first as! UIStackView?
return view
}
Create a viewController.Add a stackView to it.In StackView properties, goto 3rd bar which named as custom class, for class name give stackView class name

Use Storyboard to add AutoLayout constraints relating subviews of custom UIView to other views?

After creating a custom UIView, how can you use Storyboard to add AutoLayout constraints relating its subviews to views/components outside the custom UIView?
In the example below, SliceView represents a custom UIView. In its .xib file there is a UILabel.
After adding a new UIView and setting its class to SliceView, the new view looks the same in Storyboard. It hasn't inherited the subviews and properties of a SliceView. The two screenshots below illustrate the view hierarchy in the SliceView.xib and how after transforming a UIView into a SliceView, the hierarchy remains the same in Storyboard.
So if you want to add a button that is constrained to 10 pixels above the SliceView label, how can you do that inside Storyboard?
class SliceView: UIView {
#IBOutlet var storyboardView: UIView!
#IBOutlet weak var captionLabel: UILabel!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
if let nibsView = NSBundle.mainBundle().loadNibNamed("SliceView", owner: self, options: nil) as? [UIView] {
let nibRoot = nibsView[0]
self.addSubview(nibRoot)
nibRoot.frame = self.bounds
}
}
}
Storyboard view hierarchy:
SliceView view hierarchy:

Resources