How to initialize the different properties of Custom UITableViewCell in swift - ios

I was trying to work with custom tableview cells for the tableview in my project. Right now I have got the tableview working using the custom cell class that I have created. I am using XIB for the cell.
But now the problem is that I want to add a gradient layer & an extra label as properties to my cell class and I want to get them initialize. I am writing the code for these properties in the init() like
override init(){
//Code to initialize the properties of cell
.....
super.init()
}
And I have also have the required init() which is like this
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
But its not working its just giving an error at required init().
I know how to get this done in Objective-C but have no idea how to do this in swift. Please give me some pointers on how to get this done.
EDIT : Yeah I did try initializing the properties in the awakeFromNib() where I got error saying I can't assign to property in self. And in init(coder:) also I was unable to initialize the properties.
Thanks in advance :)

Something wrong in your code. I create new project, create custom UITableViewCell via nib, and put such code, and it works fine:
override func awakeFromNib() {
super.awakeFromNib()
self.textLabel.text = "testing cells";
self.detailTextLabel?.text = "text";
}
Did you register cell's nib in table view?
self.tableView.registerNib(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "Cell");
Also did configure right the nib in Interface Builder? Does you change UITableViewCell class to your custom class?

Related

Swift: 'super.init' isn't called on all paths before returning from initializer?

I am getting this error on the last brace of a init in a class of mine. The class looks something like the following (I market the spot where error happens):
class RecordingViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
let cameraButton:UIButton?
let camPreview:UIView?
init (cameraButton: UIButton!, camPreview: UIView!) {
self.cameraButton = cameraButton
self.camPreview = camPreview
} //get error here
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//do a bunch of other stuff
}
I have looked here and here for a solution but both seem like solutions that are either really bad or that are too specific to that question, thus they have not work for me.
I was hoping for a solution to my problem done in such a way that it can help me understand why this error is happening.
Since you inherit from UIViewController, you should call super.init right after you set the variables in your init function
When you inherit a class and implement a new init function or override its own init function you should (almost) always call super.init. Let's take your example, you inherited from UIViewController. UIViewController has a few init functions that you can use to initialize a view controller. if you don't call super.init, all the code inside those functions will not get called and possibly the view controller won't get initialized.
Anyway, this piece of code should work for you:
class ViewController: UIViewController {
var button: UIButton?
init(button: UIButton) {
self.button = button
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is what I found on Swift Programming Language:
In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.
Hope this can explain that question.

init() inside custom cell class that is delegate and datasource of collectionView

Im really confused trying to init() some stuff inside my custom cell class that i want to be the datasource and delegate of a collectionview.
How do i init() so that i have the data in an array ready to be used by cellForItemAt??
var partArray : [CollectionStruct] = []
init(partArray: [CollectionStruct]) {
super.init(partArray: [CollectionStruct])
innerCollectionView.delegate = self
innerCollectionView.dataSource = self
//innerCollectionView.tag = item
// add some stuff from local sql lite to array
// this is how i normally do this in viewDidLoad
// but cant use that in cell subclass
BuildArray.buildArrayFromQuery(queryForCollection: "Part", delegateSender: "DownloadPack", completeBlock: { (result) in
if result.isEmpty == false {
self.partArray = result
}
})
}
If i just do:
init() {
// stuff for the array
}
I get swift suggesting i include this block:
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
You can't create your own initializers for the UITableViewCell. The only two initializers you can override are one that recieves a frame (init(frame)) and one that recieves a code (init(coder)).
In order to find a solution for your problem, you can a use either a property or a method in which recieves the data array. Either one would be called or set after the cell is created. You make your setup, and afterwards you call reloadData() of the collectionView.
If you decide to use a property, you could do this mentioned logic in the didSet of the property.

Swift - Subclassed UICollectionViewCell not loading IBOutlet properties

I come from Obj-C and I'm struggling on doing something super basic in Swift!
I have a custom UICollectionViewCell:
class CustomCell: UICollectionViewCell
{
// Outlets
// ***************************
#IBOutlet weak var button: UIButton!
// Init
// ***************************
required init?(coder aDecoder: NSCoder)
{
super.init(coder:aDecoder)
setup()
}
override init(frame: CGRect)
{
super.init(frame: frame)
setup()
}
func setup()
{
button.backgroundColor = .white
}
}
The cell is loaded from an external .xib file, so init(coder:) is called for the initialization but my button is not ready.
If I change to button?.backgroundColor the app doesn't crash but obviously nothing happen.
I can call my setup() function in layoutSubviews() and it works, but it's definitely not the right place to be.
How do I solve this massive problem? lol
Edit
Probably I have to call setup() from awakeFromNib(), right?
I usually don't use external .xib, I'm not familiar with them
Edit: Sorry It seems youe edited your question before my answer, it seems as you load it from XIB, then you can run the awakeFromNib which will be called when you register a nib using this method:
Apple Source UICollectionView
Apple Source UITableView
--- old post below ---
In Xcode 6 you have to provide additional init(coder:) initializer in
classes like RDCell, which is the subclass of UICollectionViewCell.
This initializer is called instead of init(frame:) when the class gets
initialized from a storyboard or a xib file. That’s not our case, but
we still need to provide init(coder:). We can use the solution
provided to us by Xcode. In Issue Navigator click on an error that
says “'required' initializer 'init(coder:)' must be provided by
subclass of 'UICollectionViewCell'“,
Source

xcode 7 swift found nil while unwrapping optional in a table view cell

I'm sorry to be asking yet another "found nil unwrapping optional in TableViewCell" posting, but after hours of debugging and reading other such posts here, I'm still stuck. I think I've covered all of the usual mistakes, but still can't get things to work.
As a starting point, everything works fine as long as I use the default label in the cell, referenced with "cell.textLabel!.text = mystring." And I have another case where I customized the prototype cell with an image and a label, and that worked fine too.
In the current case, I have a normal UIViewController, with some other views and a UITableView embedded. The normal UIViewController is set as the delegate for the cells.
Here's the SettingsCell.swift code. Pretty simple. The little dot on the left of the Outlet in xcode is solid, showing that the outlet is properly connected to the CellTwo label on my prototype table cell.
class SettingsCell: UITableViewCell {
#IBOutlet weak var CellTwo: UILabel!
override init(style: UITableViewCellStyle, reuseIdentifier reuseID: String?) {
super.init(style: style, reuseIdentifier: reuseID)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In the corresponding SettingsVC.swift view controller, here is the dequeueCell code that takes the nil fault on trying to set the cell.CellTwo value.
The cells display fine in the table view, but only the default textLabel.txt that is part of the UILabel shows up. My custom label does not show at all.
func tableView(
tv: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tv.dequeueReusableCellWithIdentifier("SettingsCell") as! SettingsCell
let row = indexPath.row
cell.textLabel!.text = SettingsNames[row]
// the commented out line below crashes on a nil unwrapped optional
// cell.CellTwo!.text = "Blah"
if let b = cell.CellTwo {
b.text = "Blah"
}
return cell
}
With the little "let b =" optional unwrap nil protection, the app does not crash, but my second label doesn't get set either. Finally, consider registration of the class.
Many examples exist on the net (without using prototypes) where you register the class, and I've done that before successfully. And I've done an example where I built a custom prototype cell, and that one worked fine too. But... I think all those examples were using a UITableViewController, not a normal UIViewController + embedded UITableView. Maybe that has something to do with it.
Anyhow, here is the init / viewDidLoad code in the UIViewController that contains the UITableView. As you can see from the code, the UIViewController claims the datasource and delegate responsibilities, and sure enough, the table rows show up and work fine.
I've tried registration both ways -- without the code registration, and with the code registration line. But it doesn't make any difference at all - my code still crashes on the nil reference to my custom label in the cell.
class SettingsVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
SettingsTV.dataSource = self
SettingsTV.delegate = self
// I tried with this line in and out, no difference, the crash still happens
//SettingsTV.registerClass(SettingsCell.self, forCellReuseIdentifier: "SettingsCell")
SettingsTV.estimatedRowHeight = 70
}
It's like my custom label doesn't get created or initialized somehow. But I can't explain it. Any ideas? Thanks
The answer to my issue was in the initialization of the SettingsCell shown in the first block of code above. That initialization sequence was copied from an example that worked fine, but that DID NOT USE THE STORYBOARD. Instead, that example just limited itself to using the default textLabel.text fields in the UITableViewCell definition.
To solve the problem, I just kept going back through my examples trying to recreate the problem in examples that already worked. Here's the key.
If you use the storyboard with a prototype cell, the table cell initialization cannot look like the first block in this posting!! That style only works for non-storyboard cells.
Instead, it should look like this one below. Notice that I commented out the default code that xcode inserts into the file when it is created. So you can basically have an empty class for the cell, as long as you have places for the Outlets.
class SettingsCell: UITableViewCell {
#IBOutlet weak var XXXLabel: UILabel!
#IBOutlet weak var CellTwo: UILabel!
// override func awakeFromNib() {
// super.awakeFromNib()
// // Initialization code
// }
//
// override func setSelected(selected: Bool, animated: Bool) {
// super.setSelected(selected, animated: animated)
//
// // Configure the view for the selected state
// }
}
A second thing that showed up was that the two labels on the storyboard were placed at either end of the any-any size storyboard. And my constraints put the right hand label off the edge of the simulated device. I happened to rotate the device in the simulator, and presto! there was the second label. I should use the previewer more regularly. :-)
Have you tried defining your custom cell in a seperate XIB instead of using prototype cells in the tableview? It is pretty much the same as what you did but it just has another XIB file out of the storyboard. To create the XIB:
File -> New File -> iOS -> User Interface ->Empty
Drag a UITableViewCell to the XIB and customize accordingly. Set its class to SettingsCell and wire up the labels, etc... to your SettingsCell class
Then in your VC you register it like this:
let nibName = UINib(nibName: "SettingsCell", bundle:nil)
self.SettingsTV.registerNib(nibName, forCellReuseIdentifier: "SettingsCell")
Another part of the answer is in the contents of the NSCoder required init block shown at the top of this post. That block was copied from a working example that did NOT use a storyboard prototype cell. So I often took a fatal error when I tried that with my prototype tableview cell.
Here is what that block SHOULD (must) look like if you are using a prototype cell. The required NSCoder must call the super.init, like so:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//fatalError("init(coder:) has not been implemented")
}
It's odd that when xcode auto-fixes the NSCoder issue by injecting a template NSCoder required init? function, it injects one that will break with prototype cells. Why not add the super.init(..) line instead of the fatal error line? Probably there's a reason, but not one that I can see.
Anyhow, all this works for me now, even with a prototype storyboard table view. So I think that's all the questions/issues resolved for this one.

Providing my own initializer for UICollectionViewFlowLayout

I'd like to provide my own initializer for my UICollectionViewFlowLayout subclass, for the purposes of calculating my itemSize property at runtime to scale my cells properly for different screen sizes. I understand that the simplest way to do so, is to adjust the default UICollectionViewFlowLayout from the UICollectionViewController like this:
//in UICollectionViewController
override func viewDidLoad() {
super.viewDidLoad()
let someWidth = ...
let someHeight = ...
let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: someWidth, height: someHeight)
}
However, I need to subclass UICollectionViewFlowLayout for the purposes of some custom layout for my UICollectionReusableView, so I thought it would be nice to declare the itemSize property in my layout subclass.
I am trying to make my own initializer, like so:
override init() {
super.init()
itemSize = CGSizeMake(someWidth, someHeight)
}
but I am greeted with the requirement:
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
When I attempt to load this viewController, I'm greeted with the fatalError. Next, I try to simply let the super implementation deal with it:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
I recall various bits of information that is forcing my mind in a state of caution, something along the lines of: If one decides to subclassing something that requires the NSCoding things, one must do something to the variables that I added to the subclass.
Can anyone advise me on this?
-- EDIT --
Is this what you're looking for?
class HmIsThisOK: UICollectionViewFlowLayout {
var myCustomSize: CGSize = CGSizeMake(60.0, 23.5)
override var itemSize: CGSize {
get { return myCustomSize }
set { myCustomSize = newValue }
}
}
or does it just make sense to follow the usual protocol of initializing itemSize in your implementation of prepareForLayout()?
If none of that works for you, it's perfectly safe to do the required init junk and just add your code to the end of it after calling super.init(...). You're not concerned with tying your custom init to the coding/encoding process, which has to do from awakening from the Nib file (usually Main.storyboard). Because the two inits() are required, you usually factor out your custom init code into a helper method.
Finally, If you are indeed awakening this object from the Nib file, you can use an overridden awakeFromNib() function to do custom initialization, without worrying about the init() prickliness.

Resources