Custom TableView Cell is not Clickable - swift

I have created a custom tableview cell by creating a separate class for it. The code works fine with the only exception being that if I tap the label in the cell, the cell does not register that it was selected. However, when the image in the cell is tapped, the cell registers it perfectly fine. I have included the cell's class's implementation below. I would really appreciate it if someone could help me.
class ItemCustomCell: UITableViewCell {
var message: String?
var itemImage: UIImage?
var messageView: UITextView = {
var text = UITextView()
return text
}()
var itemImageView: UIImageView = {
var itemImage = UIImageView()
return itemImage
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.addSubview(messageView)
self.addSubview(itemImageView)
itemImageView.translatesAutoresizingMaskIntoConstraints = false
itemImageView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
itemImageView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10).isActive = true
itemImageView.widthAnchor.constraint(equalTo: self.heightAnchor).isActive = true
itemImageView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10).isActive = true
messageView.translatesAutoresizingMaskIntoConstraints = false
messageView.leftAnchor.constraint(equalTo: self.itemImageView.rightAnchor, constant: 25).isActive = true
messageView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 20).isActive = true
messageView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
let fixedWidth = messageView.frame.size.width - 50
let newSize = messageView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
messageView.heightAnchor.constraint(equalToConstant: newSize.height).isActive = true
}
override func layoutSubviews() {
super.layoutSubviews()
if let message = message {
messageView.text = message
messageView.font = UIFont(name: messageView.font!.fontName, size: 15)
messageView.isEditable = false
messageView.isScrollEnabled = false
}
if let image = itemImage {
itemImageView.image = image
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Since you're using UIextView for your label and not UILabel your text view catches taps earlier than the cell. Set messageView.isUserInteractionEnabled = false to avoid this behaviour.

Related

How to make long text label, which lives in UI View, to multiple lines in Swift?

I'm trying to make a long text label, which lives inside of UI View, to multiple lines. I was searching for a solution for 2 hours, but I couldn't solve it, so I want to ask for some help. Here is the image of what I get now.
This is the View Hierarchy debugger.
In a table view cell, I put the repository name which I got from Github. In the third cell in the image, I want to make the repo name to two lines, since it's long. I saw this StackOverflow question, which sounds similar to my current problem and implemented in my code, but it didn't work.
Here is my code. It's too long but the point is, I have declared label.numberOfLines = 0 and label.adjustsFontSizeToFitWidth = true, and I tried to make my label convertible to multiple lines, but it didn't work. Could anyone please point me at what I'm doing wrong here?
import UIKit
class RepositoryCell: UITableViewCell {
//MARK: - Properties
let userImageView: UIImageView = {
let img = UIImageView()
img.contentMode = .scaleAspectFit
img.clipsToBounds = true
img.backgroundColor = .black
return img
}()
let containerView: UIView = {
let view = UIView()
view.clipsToBounds = true
view.backgroundColor = .systemPink
return view
}()
let userNameLabel: UILabel = {
let label = UILabel()
label.textColor = .black
label.font = UIFont.boldSystemFont(ofSize: 20)
label.numberOfLines = 0
label.adjustsFontSizeToFitWidth = true
return label
}()
let repositoryNameLabel: UILabel = {
let label = UILabel()
label.textColor = .gray
label.font = UIFont.systemFont(ofSize: 14)
label.numberOfLines = 0
label.adjustsFontSizeToFitWidth = true
return label
}()
let starNumLabel: UILabel = {
let label = UILabel()
label.textColor = .systemPink
label.font = UIFont.systemFont(ofSize: 14)
label.backgroundColor = .black
return label
}()
//MARK: - Init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
addSubview(userImageView)
containerView.addSubview(userNameLabel)
containerView.addSubview(repositoryNameLabel)
addSubview(containerView)
addSubview(starNumLabel)
configureUserNameLabel()
configureRepositoryNameLabel()
configureViewConstraints()
}
override func layoutSubviews() {
super.layoutSubviews()
userImageView.layer.cornerRadius = userImageView.frame.height / 2
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Helper functions
func configureCellView(repository: Repository) {
userImageView.image = UIImage(named: "001")
userNameLabel.text = repository.userName
repositoryNameLabel.text = repository.repositoryName
starNumLabel.text = "⭐️\(String(describing: repository.starNum))"
}
func configureUserNameLabel() {
userNameLabel.numberOfLines = 0
userNameLabel.adjustsFontSizeToFitWidth = true
}
func configureRepositoryNameLabel() {
repositoryNameLabel.numberOfLines = 0
repositoryNameLabel.adjustsFontSizeToFitWidth = true
}
func configureViewConstraints() {
setUserImageConstraints()
setContainerViewConstraints()
setUserNameLabelConstraints()
setRepositoryNameLabel()
setStarNumLabel()
}
func setUserImageConstraints() {
userImageView.translatesAutoresizingMaskIntoConstraints = false
userImageView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
userImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
userImageView.heightAnchor.constraint(equalToConstant: 70).isActive = true
userImageView.widthAnchor.constraint(equalTo: userImageView.heightAnchor, multiplier: 1).isActive = true
}
func setContainerViewConstraints() {
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
containerView.topAnchor.constraint(equalTo: topAnchor, constant: 10).isActive = true
containerView.leadingAnchor.constraint(equalTo: userImageView.trailingAnchor, constant: 10).isActive = true
containerView.trailingAnchor.constraint(equalTo: starNumLabel.leadingAnchor, constant: -10).isActive = true
}
func setUserNameLabelConstraints() {
userNameLabel.translatesAutoresizingMaskIntoConstraints = false
userNameLabel.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
userNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
// userNameLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
}
func setRepositoryNameLabel() {
repositoryNameLabel.translatesAutoresizingMaskIntoConstraints = false
repositoryNameLabel.topAnchor.constraint(equalTo: userNameLabel.bottomAnchor).isActive = true
repositoryNameLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
// repositoryNameLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
// this doesn't work...
// repositoryNameLabel.trailingAnchor.constraint(greaterThanOrEqualTo: containerView.leadingAnchor, constant: 5).isActive = true
}
func setStarNumLabel() {
starNumLabel.translatesAutoresizingMaskIntoConstraints = false
starNumLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
starNumLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
}
}
My guess is the trailing constraints of your label is not set properly.
You can use the View Hierarchy debugger in Xcode to have a clear view of the size your view actually has at runtime.
If the trailing anchor is not set, wrapping text isn't possible as it doesn't reach the "end" of the view.

The last cell in UITableView always has the same height as the tallest cell in the table?

I have a UITableView that displays comments. The last cell always has the same height as the tallest cell in the UITableView and I do not know why. With reference to the image below, it can be seen that the height of the last custom object (LabelWithPadding) that holds the comment is the same height as the tallest custom object in the same table. Please can someone advise?
class CommentOnPostTableView: UITableView, UITableViewDataSource, UITableViewDelegate{
var items: [Comment]?
var post: ResearchPost?
let cellIDB = "commentCellId"
init(frame: CGRect, style: UITableView.Style, sourceData: [Comment], postContent: ResearchPost) {
super.init(frame: frame, style: style)
items = sourceData
post = postContent
self.dataSource = self
self.delegate = self
self.register(CommentTableViewCell.self, forCellReuseIdentifier: cellIDB)
//self.separatorStyle = .none
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
/**Delegate functions*/
extension CommentOnPostTableView{
/***Number of cells*/
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let dataSource = items{
return dataSource.count
}else{
return 0
}
}
/***Create Cell*/
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let dataSource = items else{return UITableViewCell.init()}
let cell = tableView.dequeueReusableCell(withIdentifier: cellIDB, for: indexPath) as! CommentTableViewCell
cell.dataObject = dataSource[indexPath.row]
return cell
}
class CommentTableViewCell: UITableViewCell {
var commentLabel: LabelWithPadding!
var imgView: UIImageView = {
let view = UIImageView()
view.backgroundColor = UIColor.black
view.contentMode = .scaleAspectFit
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
var nameLabel: UILabel = {
let label = UILabel()
label.font = .boldSystemFont(ofSize: 11)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var dataObject: Comment?{
didSet{
if let data = dataObject{
guard
let image = data.imageOfCommentor,
let name = data.name,
let comment = data.comment else{return}
imgView.image = image
nameLabel.text = name
let padding = UIEdgeInsets.init(top: 3, left: 5, bottom: 3, right: 5)
commentLabel = LabelWithPadding.init(frame: .zero, with: padding)
commentLabel.translatesAutoresizingMaskIntoConstraints = false
commentLabel.label.text = comment
setupViews()
}
}
}
....
// Auto layout constraints
private func setupViews(){
let c = contentView.safeAreaLayoutGuide
contentView.addSubview(imgView)
contentView.addSubview(nameLabel)
contentView.addSubview(commentLabel)
imgView.topAnchor.constraint(equalTo: c.topAnchor, constant: borderSpace).isActive = true
imgView.leadingAnchor.constraint(equalTo: c.leadingAnchor, constant: borderSpace).isActive = true
imgView.widthAnchor.constraint(equalTo: c.widthAnchor, multiplier: 0.08).isActive = true
imgView.heightAnchor.constraint(equalTo: c.widthAnchor, multiplier: 0.08).isActive = true
nameLabel.topAnchor.constraint(equalTo: c.topAnchor, constant: borderSpace).isActive = true
nameLabel.leadingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: 10).isActive = true
nameLabel.trailingAnchor.constraint(equalTo: c.trailingAnchor, constant: -borderSpace).isActive = true
commentLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: viewSpace).isActive = true
commentLabel.widthAnchor.constraint(equalTo: nameLabel.widthAnchor).isActive = true
commentLabel.trailingAnchor.constraint(equalTo: c.trailingAnchor, constant: -borderSpace).isActive = true
commentLabel.bottomAnchor.constraint(equalTo: c.bottomAnchor).isActive = true
commentLabel.label.font = UIFont.systemFont(ofSize: 12)
commentLabel.label.numberOfLines = 0
commentLabel.label.lineBreakMode = .byWordWrapping
commentLabel.layer.cornerRadius = 7
commentLabel.layer.masksToBounds = true
commentLabel.backgroundColor = UIColor.appGrayExtraLightGray
}
}
Custom Class:
class LabelWithPadding: UIView {
var padding: UIEdgeInsets!
let label: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
init(frame: CGRect, with padding: UIEdgeInsets) {
super.init(frame: frame)
self.padding = padding
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView(){
self.addSubview(label)
label.topAnchor.constraint(equalTo: self.topAnchor, constant: padding.top).isActive = true
label.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -padding.bottom).isActive = true
label.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: padding.left).isActive = true
label.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -padding.bottom).isActive = true
}
}
First of all, I couldn't find where you update constraints.
When you use tableView.dequeueReusableCell, you may get the old cell, so you should update old constraints (or remove old constratints and add new ones). You may read it in docs.
You should add variable to store old constraints:
var oldConstraints: [NSLayoutConstraint] = []
And should deactivate old constraints and add new constraints in setupView():
NSLayoutConstraint.deactivate(oldConstraints)
let constraints = [
imgView.topAnchor.constraint(equalTo: c.topAnchor, constant: borderSpace),
imgView.leadingAnchor.constraint(equalTo: c.leadingAnchor, constant: borderSpace),
imgView.widthAnchor.constraint(equalTo: c.widthAnchor, multiplier: 0.08),
imgView.heightAnchor.constraint(equalTo: c.widthAnchor, multiplier: 0.08),
nameLabel.topAnchor.constraint(equalTo: c.topAnchor, constant: borderSpace),
nameLabel.leadingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: 10),
nameLabel.trailingAnchor.constraint(equalTo: c.trailingAnchor, constant: -borderSpace),
commentLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: viewSpace),
commentLabel.widthAnchor.constraint(equalTo: nameLabel.widthAnchor),
commentLabel.trailingAnchor.constraint(equalTo: c.trailingAnchor, constant: -borderSpace),
commentLabel.bottomAnchor.constraint(equalTo: c.bottomAnchor)
]
NSLayoutConstraint.activate(constraints)
oldConstraints = constraints
Also you shouldn't add subviews every time you update dataObject in CommentTableViewCell, because they could have already been added.
In conclusion, you should add subview to contentView just once and update the constraints every time you update dataObject. I gave you an example how to solve your issue.
You could also use prepareForReuse() to prepare the cell for reuse:
If a UITableViewCell object is reusable—that is, it has a reuse
identifier—this method is invoked just before the object is returned
from the UITableView method dequeueReusableCellWithIdentifier:.

Cannot override with a stored property 'leadingConstraint' , Overriding non-open var outside of its defining module xcode 10

I'm getting this weird error. My Project was working fine and all of a sudden I got these errors. The errors indicated by Xcode:
Cannot override with a stored property 'leadingConstraint'
Overriding non-open var outside of its defining module
I've defined a constraint variable in UITableViewCell, on which it's giving error.
I tried
clean the build folder.
Restarted the Xcode but it's still giving me
that error.
Error
Cell:
import UIKit
class ReceiverChatImageCell: UITableViewCell {
let bubbleBackgroundView: UIView = {
let v = UIView()
v.backgroundColor = UIColor.darkGray
v.layer.cornerRadius = 5
v.layer.masksToBounds = true
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
public let chatImage: UIImageView = {
let v = UIImageView()
v.contentMode = .scaleAspectFill
v.translatesAutoresizingMaskIntoConstraints = false
v.clipsToBounds = true
return v
}()
var maxMessageLength: CGFloat = 250
var leadingConstraint: NSLayoutConstraint?
var chatMessage: ChatModel! {
didSet {
profileImageView.loadImage(string: chatMessage.imageUrl)
chatImage.loadImage(string: chatMessage.chatImageUrl)
}
}
public let profileImageView: UIImageView = {
let v = UIImageView()
v.image = #imageLiteral(resourceName: "babs")
v.translatesAutoresizingMaskIntoConstraints = false
v.contentMode = .scaleAspectFill
v.clipsToBounds = true
v.layer.cornerRadius = 20
return v
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit(){
backgroundColor = .clear
addSubview(profileImageView)
addSubview(bubbleBackgroundView)
addSubview(chatImage)
let available: CGFloat = UIScreen.main.bounds.width - 100
if maxMessageLength > available{
maxMessageLength = available - 50
print("Available Space \(maxMessageLength)")
}
// lets set up some constraints for our image
let constraints = [
profileImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0),
profileImageView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8),
profileImageView.widthAnchor.constraint(equalToConstant: 40),
profileImageView.heightAnchor.constraint(equalToConstant: 40),
chatImage.topAnchor.constraint(equalTo: topAnchor, constant: 8),
chatImage.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -24),
chatImage.widthAnchor.constraint(lessThanOrEqualToConstant: maxMessageLength),
chatImage.heightAnchor.constraint(lessThanOrEqualToConstant: 250),
bubbleBackgroundView.topAnchor.constraint(equalTo: chatImage.topAnchor, constant: -8),
bubbleBackgroundView.leadingAnchor.constraint(equalTo: chatImage.leadingAnchor, constant: -16),
bubbleBackgroundView.bottomAnchor.constraint(equalTo: chatImage.bottomAnchor, constant: 8),
bubbleBackgroundView.trailingAnchor.constraint(equalTo: chatImage.trailingAnchor, constant: 16),
]
NSLayoutConstraint.activate(constraints)
leadingConstraint = chatImage.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 60)
leadingConstraint?.isActive = true
}
}
I haven't used this variable outside the cell.
How can I solve this problem?

Custom cell and stack-view programmatically

I am creating a tableview with a custom cell programmatically. I would like to utilise a stack view with arranged subviews within the custom cell. However, all my efforts have failed. Firstly is it possible to do this?
Secondly I am placing the code after:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: "cellId")
I am using this code to create the stack view:
let thestack: UIStackView = {
let sv = UIStackView()
sv.distribution = .fillEqually
sv.axis = .vertical
sv.spacing = 8
return sv
}()
But I can't add the arranged subviews to this in addition to which after I addsubview(thestack) and list all the constraints - none of my data shows in the custom cell. Any help would be appreciated.
Yes, it is possible. Follow like below:
class CustomTableViewCell: UITableViewCell {
let stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.spacing = 10
stackView.distribution = .fillEqually
return stackView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: reuseIdentifier)
addSubview(stackView)
stackView.leftAnchor.constraint(equalTo: leftAnchor, constant: 10).isActive = true
stackView.topAnchor.constraint(equalTo: topAnchor, constant: 10).isActive = true
stackView.rightAnchor.constraint(equalTo: rightAnchor, constant: -10).isActive = true
stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
let redView = UIView()
redView.backgroundColor = .red
let yellowView = UIView()
yellowView.backgroundColor = .yellow
let blackView = UIView()
blackView.backgroundColor = .black
stackView.addArrangedSubview(redView)
stackView.addArrangedSubview(yellowView)
stackView.addArrangedSubview(blackView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Label text in center and moving depending on number of text lines in the label below

My UILabel is pinned to the top of the cell, just like UIImage but if the text below has f.e. 1 line (its also in UILabel) my top UILabel is in other place.
Here are my constraints and labels declaration. Thanks to anyone who will try to solve my problem :) I tried using sizeToFit method on my UILabels
var albumImage: UIImageView = {
let view = UIImageView()
view.translatesAutoresizingMaskIntoConstraints = false
view.contentMode = .scaleAspectFit
return view
}()
var albumName: UILabel = {
let view = UILabel()
view.translatesAutoresizingMaskIntoConstraints = false
view.numberOfLines = 0
view.sizeToFit()
return view
}()
var bandName: UILabel = {
let view = UILabel()
view.translatesAutoresizingMaskIntoConstraints = false
view.sizeToFit()
return view
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
drawLayout()
}
required init?(coder aDecoder: NSCoder) {
fatalError("ERROR")
}
func drawLayout(){
let guide = self.safeAreaLayoutGuide
//albumImage
addSubview(albumImage)
addSubview(albumName)
addSubview(bandName)
albumImage.heightAnchor.constraint(equalToConstant: 50).isActive = true
albumImage.widthAnchor.constraint(equalToConstant: 50).isActive = true
albumImage.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: 8).isActive = true
albumImage.topAnchor.constraint(equalTo: self.topAnchor, constant: 8).isActive = true
albumImage.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8).isActive = true
//albumTitle
albumName.topAnchor.constraint(equalTo: bandName.bottomAnchor).isActive = true
albumName.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8).isActive = true
albumName.leadingAnchor.constraint(equalTo: albumImage.trailingAnchor, constant: 8).isActive = true
albumName.trailingAnchor.constraint(equalTo: guide.trailingAnchor, constant: -8).isActive = true
//bandName
bandName.topAnchor.constraint(equalTo: albumImage.topAnchor).isActive = true
bandName.leadingAnchor.constraint(equalTo: albumImage.trailingAnchor, constant: 8).isActive = true
}