Must call a designated initializer of the superclass 'UITableViewCell' - ios

let bubbleView : UIView = {
let view = UIView()
view.backgroundColor = blueColor
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 16
view.layer.masksToBounds = true
return view
}()
let messageImageView : UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.cornerRadius = 16
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFill
return imageView
}()
init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// getting error in like "super.init(frame: frame)" as Must call a designated initializer of the superclass 'UITableViewCell'please help me in sorting this problem thanks in advance...

I guess the code that you provided is from UITableViewCell type class. So in the initializer you should call designed initializer for this class. Not from UIView
The designated initializer for UITableViewCell class is
init(style: UITableViewCellStyle, reuseIdentifier: String?)
So in you class you should override this initializers:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}

From the docs for init(style: UITableViewCellStyle, reuseIdentifier: String?):
This method is the designated initializer for the class.
The super initializer you're calling is for UIView, not UITableViewCell.

Related

Custom UITableViewCell with title - constraints

I have a custom UITableViewCell so that
class CustomCell: UITableViewCell {
private var someCustomView = UIView()
init(style: UITableViewCell.CellStyle, reuseIdentifier: String?, text: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupContraints()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func setupContraints() {
contentView.addSubview(someCustomView)
self.someCustomView.frame.size = CGSize(width: 100, height: 100)
NSLayoutConstraint.activate([
self.someCustomView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
self.someCustomView.topAnchor.constraint(equalTo: contentView.topAnchor)
])
setNeedsLayout()
layoutIfNeeded()
}
The issue I am having is around the NSLayoutConstraint - Ignore the specifics of this as I haven't yet setup the constraints I need, however the view only setting up the frame and not constraints. Any help? Thank you!
You need to set translatesAutoresizingMaskIntoConstraints to false before adding constraints as follows:
someCustomView.translatesAutoresizingMaskIntoConstraints = false

How do you call an IBOutlet element's initializer?

I wrote the following subclass of UITextField:
var imageView: UIButton? = nil
var options: [String]? = nil
let padding = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 30)
override init(frame: CGRect) {
super.init(frame:frame)
self.backgroundColor = Constants.darkPurple
self.textColor = .white
self.layer.cornerRadius = 10
self.clipsToBounds = true
self.translatesAutoresizingMaskIntoConstraints = false
self.tintColor = .clear
Constants.styleDropDownField(self)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//fatalError("init(coder:) has not been implemented")
}
}
When I add instances of this class programmatically, it works fine. But when I add an instance of the class in the storyboard and then connect it with an IBAction, it's just a blank white text field without any of the properties I assigned in the class's initializer - it seems that the initializer isn't being called at all. Is there any way to call the element's initializer? Or is there another function, similar to viewDidLoad, that will run when the text field is loaded?
You'll have to call the implementation given in init(frame:) in init(coder:) because this method is called when used from the storyboard. Here is the code:
override init(frame: CGRect) {
super.init(frame:frame)
initialSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialSetup()
}
func initialSetup() {
self.backgroundColor = Constants.darkPurple
self.textColor = .white
self.layer.cornerRadius = 10
self.clipsToBounds = true
self.translatesAutoresizingMaskIntoConstraints = false
self.tintColor = .clear
Constants.styleDropDownField(self)
}
You can't call the initialiser of the component added in storyboard. But you are going in right direction. Create a common method to set these properties.
func commonSetup() {
self.backgroundColor = Constants.darkPurple
self.textColor = .white
self.layer.cornerRadius = 10
self.clipsToBounds = true
self.translatesAutoresizingMaskIntoConstraints = false
self.tintColor = .clear
Constants.styleDropDownField(self)
}
And call this method from three different methods
override func awakeFromNib() {
super.awakeFromNib()
self.commonSetup()
}
override init(frame: CGRect) {
super.init(frame:frame)
self.commonSetup()
}
//This method will be called when component is initialised from storyboard.
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonSetup()
}

Why is init(frame: CGRect) not being called in my custom UIView class?

I have a subclass of UIView called GradientView which I'm using in a UITableViewCell
layoutSubviews() is called, but my init method is never called.
import UIKit
class GradientView: UIView {
var gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame:frame)
self.backgroundColor = .orange
gradientLayer.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
gradientLayer.locations = [0.6, 1.0]
gradientLayer.frame = self.bounds
self.layer.insertSublayer(gradientLayer, at: 0)
}
override func layoutSubviews() {
super.layoutSubviews()
self.gradientLayer.frame = self.bounds
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
What am I missing?
UITableViewCell is not usually initialized with init(frame:), it's initialized with either init(style: UITableViewCellStyle, reuseIdentifier: String?) or required init?(coder aDecoder: NSCoder).
The first one will be used when you register a class with your table view, and the second one will be used when it's initialized from a storyboard or when you register a xib file with your table view.

Constraints in UITableViewCell not being calculated

I have the following cell with constraints being setup programmatically:
class RadioButtonCell: UITableViewCell {
static let identifier = "RadioButtonCell"
let radioButton = RadioButton()
let labelTitle = UILabel()
private var didUpdateConstraints = false
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupSubViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupSubViews() {
radioButton.translatesAutoresizingMaskIntoConstraints = false
labelTitle.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(radioButton)
contentView.addSubview(labelTitle)
}
override func updateConstraints() {
super.updateConstraints()
if !didUpdateConstraints {
radioButton.anchor(leading: contentView.leadingAnchor, padding: UIEdgeInsets(top: 0, left: Constants.UI.defaultMarginX2, bottom: 0, right: 0))
radioButton.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
labelTitle.anchor(leading: radioButton.trailingAnchor, padding: UIEdgeInsets(top: 0, left: Constants.UI.defaultMarginX2, bottom: 0, right: 0))
labelTitle.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
didUpdateConstraints = true
}
}
}
The anchor method is just a helper method to add constraints. The constraints are setup correctly (no issues with Autolayout).
Then in the cellForRowAtIndexPath method I create the cell like so:
guard let cell = tableView.dequeueReusableCell(withIdentifier: RadioButtonCell.identifier, for: indexPath) as? RadioButtonCell else { return UITableViewCell() }
radtioButtonController.addButton(cell.radioButton)
cell.labelTitle.text = "test"
return cell
This creates me the following layout in the tableView (which is obviously wrong):
If I move the setup of the constraints to the setupSubViews() method, the layout is correct:
class RadioButtonCell: UITableViewCell {
static let identifier = "RadioButtonCell"
let radioButton = RadioButton()
let labelTitle = UILabel()
private var didUpdateConstraints = false
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupSubViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupSubViews() {
radioButton.translatesAutoresizingMaskIntoConstraints = false
labelTitle.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(radioButton)
contentView.addSubview(labelTitle)
radioButton.anchor(leading: contentView.leadingAnchor, padding: UIEdgeInsets(top: 0, left: Constants.UI.defaultMarginX2, bottom: 0, right: 0))
radioButton.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
labelTitle.anchor(leading: radioButton.trailingAnchor, padding: UIEdgeInsets(top: 0, left: Constants.UI.defaultMarginX2, bottom: 0, right: 0))
labelTitle.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
}
Why is this like that? I thought we should setup constraint in the updateConstraints method...
Thank you for an answer :)
Edit I found out, that it works with updateConstraints when I call cell.updateConstraintsIfNeeded() or cell.setNeedsUpdateConstraints() in cellForRowAtIndexPath. Why do we need to tell the cell to calculate the constraints again? We do not need to do that when adding the constraints with IB...
Well, in your first example you haven't just added the constraints to your UI elements in the init. You just set up your sub views.
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupSubViews()
// No explicit constraint setup happened
}
To setup your constraints you've overridden the override func updateConstraints() { ... } method. Let's take a look at the official documentation from Apple.
In short, your override will be in effect when you notify the system that you need constraints' update. As a result you need to explicitly inform the system by invoking setNeedsUpdateConstraints() or updateConstraintsIfNeeded().
Let's look at your second example. You embedded the constraints setup inside your private func setupSubViews(){ ... }. So at the time this function gets called, your constraints are ready to be applied. No system call needed.

Instantiate custom button from code and Storyboard - how to make an init method

I would like to create a button like this:
import UIKit
class EKLikeButton: UIButton {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.cornerRadius = 5.0;
self.layer.borderColor = UIColor.redColor().CGColor
self.layer.borderWidth = 1.5
self.backgroundColor = UIColor.blueColor()
self.tintColor = UIColor.whiteColor()
}
}
but the only way to make it seems to be to set a pre-existing button in a Storyboard. I'd like to be able to do:
let btn = EKLikeButton()
btn.frame=CGRectMake(10.0, 10.0, 40.0, 40.0)
but when I try the above, I get
Missing argument for parameter 'coder' in call
How would I make an init function that can handle both from code or from storyboard in Swift?
This is what I usually do
class EKLikeButton: UIButton {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
init(){
super.init(frame: CGRectZero)
setUp()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUp()
}
func setUp(){
self.layer.cornerRadius = 5.0;
self.layer.borderColor = UIColor.redColor().CGColor
self.layer.borderWidth = 1.5
self.backgroundColor = UIColor.blueColor()
self.tintColor = UIColor.whiteColor()
}
}
That error message is telling you that it's looking for that coder param, because you only have that one init function. You haven't declared an initializer with no parameters, so you can't init like: EKLikeButton()
To add an init that accepts a frame parameter, you need to also implement:
override init(frame: CGRect) {
super.init(frame: frame)
// set up your frame, init, whatever
}
Then you can instantiate it like this:
let btn = EKLikeButton(CGRect(10, 10, 40, 40))

Resources