IBDesignable never finishes updating UITableViewCell in storyboard - ios

This is a similar question to this but not quite the same.
I have created a subclass of UITableViewCell which references a custom nib and marked it as #IBDesignable. Changes that are made both in code and from the .xib file display correctly in the simulator and on a device but not in the storyboard.
import UIKit
#IBDesignable class TextFieldTableViewCell: UITableViewCell {
var view: UIView!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
}
func setup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
contentView.addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "TextFieldTableView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}
The storyboard displays a permanent "Designables Updating".
Breaking the problem down into a less complex test of only subclassing a UITableViewCell and marking it as #IBDesignable results in the same permanent "Designables Updating".
import UIKit
#IBDesignable class TextFieldTableViewCell: UITableViewCell {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
}
Has anyone had success with creating a #IBDesignable UITableViewCell subclass? This is happening in Xcode 7 beta 6 and beta 5.

This issue has been resolved as of Xcode 9.
I had reported this issue to Apple and at least have the piece of mind knowing that this is a known bug. It is currently an open issue under the problem ID 17973876.
Edit: As of 12/7/2016 this bug is still marked as open.

I was having similar problems. This thread helped me understand the issue: Is there a way for Interface Builder to render IBDesignable views which don't override drawRect:
I got it to work by adding
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
layoutSubviews()
}
as far as i understand after reading the above stack link, IBDesignable loads the nib in storyboard not from nib or from coder but from the init with frame. Not too sure but I think init with style is doing a similar thing.

It seems that the "Updating" bug is happening only when the cell is inside a storyboard. Everything works OK when you create special .xib file just for the cell.
Hopefully, they will fix it soon.

Related

How to render #IBDesignable UITableViewCell in Storyboard?

I create a UITableViewCell subclass CustomTableViewCell and mark it as #IBDesignable. In CustomTableViewCell.xib file I drag a label to its center, then setup an #IBOutlet and name this label as contentLabel. Next, I drag a TableViewCell and put it on a ViewController's table view in the storyboard file, and change its class to CustomTableViewCell. My question is: how to render this contentLabel in the storyboard?
The following snippet doesn't work and I believe it's because contentLabel is not initialized yet in init(), but I want to give it an initial text and render it in the storyboard. How to achieve this?
#IBDesignable class CustomTableViewCell: UITableViewCell {
#IBOutlet weak var contentLabel: UILabel!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentLabel.text = "Hello"
}
required init?(coder: NSCoder) {
super.init(coder: coder)
contentLabel.text = "Hello"
}
}
EDIT: This cell needs to reused in several different view controllers.

How to initialize a programmatic UITableViewCell with a property via dependency injection?

I've looked around and I can't find any examples of this. I simply want to create a custom initializer on a programmatic UITableViewCell subclass that includes one property.
class CustomCell: UITableViewCell {
var foo: Foo
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// custom init with a foo
}
What I want is:
let cell = CustomCell(foo: myFoo) // other default init params if needed
Not:
let cell = CustomCell()
cell.foo = myFoo
Pointing me to a resource (if I missed one) that shows an implementation like this would be great. I assume this is a common need, but a couple hours being yelled at by init compiler errors and a few Medium articles later and I can't find anything.
Instances of UITableViewCell are created by the system, using the classic init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) initializer.
Don't forget to add this magic line after initializing your UITableView:
myTableView.register(CustomCell.self, forCellReuseIdentifier: Constant.myCellReuseIdentifier)
Let's say you write this:
class CustomCell: UITableViewCell {
convenience init(reuseIdentifier: String, customObject: Any?) {
self.init(style: UITableViewCell.CellStyle.default, reuseIdentifier: reuseIdentifier)
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Then you can create a cell with this line of code:
let cell = CustomCell(reuseIdentifier: Constant.myReuseIdentifier, customObject: nil)
... but what will you do with this cell? How to plug it into your "tableview's cells management" logic.
To inject something to your cell, you may plug in:
tableView(_:cellForRowAt:) for a UITableView (in the object implementing UITableViewDataSource)
collectionView(_:cellForItemAt:) or collectionView:willDisplayCell:forItemAtIndexPath: for a UICollectionView.
Those 3 methods may be call very frequently by the system, avoid memory allocations or any memory / CPU intensive task when possible.

UITableViewCell outlets

When do outlets get bound to a UITableViewCell instance? If I print myLabel in the debugger, it's nil and prints:
fatal error: unexpectedly found nil while unwrapping an Optional value
class MyTableViewCell: UITableViewCell {
#IBOutlet var myLabel: UILabel!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
print("\(myLabel)")
}
}
Outlets are not yet set when the init method of your tableview cell is called!
I'm not sure what your intention is:
If you want to make sure you are not unwrapping a nil optional value just use if let like this:
if let label = myLabel {
print("\(label)")
}
If you want to setup the cell of your tableView, you should first register the cell by calling tableView.registerNib(UINib(nibName: "MyTableViewCell", bundle: nil), forCellReuseIdentifier: "reuse"). Then dequeue the cell in cellForRowAtIndexPath using tableView.dequeueReusableCellWithIdentifier("reuse") and make sure you set the values of your cell in awakeFromNib:
override func awakeFromNib() {
super.awakeFromNib()
//you can be sure that myLabel is initialzed here
print("\(myLabel)")
}
If you are connecting an outlet from the Main Storyboard, your problem could arise if the outlet was accidentally deleted. Recreate the outlet and the label will not be nil anymore.
The issue is that you are running into a case where myLabel is nil. It could be while the table view cell is being recycled.

iOS UITableViewCell with custom accessoryView in InterfaceBuilder

I'm creating a settings page for my Swift app and (following examples here on SO) I've subclassed a UITableViewCell and in the init() handlers I've set the accessoryView to be a UISwitch().
I also have a custom #IBInspectable property on the subclass which is also marked #IBDesignable:
#IBDesignable class TableViewSwitchCell: UITableViewCell {
#IBInspectable var settingsKey: String?
func build()
{
accessoryView = UISwitch()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
build()
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
build()
}
// code omitted that uses the settingsKey with NSUserDefaults
}
How can I arrange for the UISwitch to also be visible in InterfaceBuilder so that I can attach additional targets to it whilst still just subclassing from UITableViewCell
Ideally I don't want to create a whole new nib with its own layout, since I still want to retain most of the other features of a standard UITableViewCell.
Drop UISwitch in Interface Builder inside of cell. Then wire it with up accessoryView of your cell. On standard basic cells the control tends to be locked in the middle of cell in IB. However in runtime it will be properly positioned.

Custom UITableViewCell on Swift

I need to create a custom UITableViewCell,
I have created a class inside of a ViewController class.
I have written the code below;
class FavoritesCell: UITableViewCell
{
var timeLabel:UILabel?
override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
super.init(style: UITableViewCellStyle.Value1, reuseIdentifier: reuseIdentifier)
timeLabel = UILabel(frame: CGRectMake(250, 10, 70, 20))
//how to add this time label as a subview of FavoritesCell
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
As i mentioned in commented line, how to add timeLabel to UITableViewCell?
Thanks
After you've created your timeLabel, this should do it:
self.contentView.addSubview(timeLabel!)
This adds the timeLabel to the cell's contentView rather than the cell itself, but that's generally what you do in table view cells.
Other things to note:
It's fine to use self here - after your super.init call, the object is fully initialised.
You've just created the timeLabel, so it's safe to force-unwrap it in addSubview.
As UITableViewCell class is derived from the UIView, so you can use the same method - self.addSubview(timeLabel!).

Resources