I have a UITableView
with custom cells inside it called NewsCell
.
Inside the NewsCell
I have a custom UIView
that is used as an action button.
Many times when the TableView
is loading a cell, the custom action button appears at the top of the cell for a few seconds until it goes back down to its right spot at the bottom of the cell. Sometimes it returns to its right place only after I scroll the cell out of the viewport.
Any idea why this is happening?
Here is a photo to make things clearer: action button out of place
NewsCell was created using .xib file and here is the relevant codes:
protocol NewsCellDelegate {
func favoriteIconDidPress(forArticle article: Article)
func actionButtonDidPress(inside article: Article)
}
enum ArticleFavoriteMark {
case selected
case notSelected
}
class NewsCell: UITableViewCell, MainActionButtonDelegate{
@IBOutlet weak var entireNewsCell: UIView!
@IBOutlet weak var newsImage: UIImageView!
@IBOutlet weak var favoriteIcon: UIImageView!
@IBOutlet weak var dateLabel: UILabel!
@IBOutlet weak var subjectTag: UIButton!
@IBOutlet weak var moreSubjectsTag: UIButton!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var authorLabel: UILabel!
@IBOutlet weak var summaryLabel: UILabel!
@IBOutlet weak var actionButton: MainActionButtonView!
var delegate: NewsCellDelegate?
var articleUrl = ""
var articleID = ""
var articleImageUrl = ""
var isFavorite: Bool = false
override func awakeFromNib() {
super.awakeFromNib()
setCellBorder()
setCellColorsDesign()
setImageRounded()
setTagsRounded()
setGestureRecognizer()
actionButton.delegate = self
}
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0))
}
func setGestureRecognizer() {
favoriteIcon.addGestureRecognizer(UITapGestureRecognizer(target: favoriteIcon, action: #selector(favoriteIconPressed)))
favoriteIcon.isUserInteractionEnabled = true
let tapGestureRecognizer1 = UITapGestureRecognizer(target: self, action: #selector(favoriteIconPressed(tapGestureRecognizer:)))
favoriteIcon.addGestureRecognizer(tapGestureRecognizer1)
}
@objc func favoriteIconPressed(tapGestureRecognizer: UITapGestureRecognizer) {
let currentArticle = Article(id: articleID, articleTitle: titleLabel.text!, date: dateLabel.text ?? "", url: articleUrl, content: summaryLabel.text!, author: authorLabel.text ?? "", topic: subjectTag.currentTitle!, imageUrl: articleImageUrl , isFavorite: isFavorite)
delegate?.favoriteIconDidPress(forArticle: currentArticle)
}
func actionButtonDidPress(btnText: String) {
let currentArticle = Article(
id: articleID,
articleTitle: titleLabel.text ?? "",
date: dateLabel.text ?? "",
url: articleUrl,
content: summaryLabel.text ?? "",
author: authorLabel.text ?? "",
topic: subjectTag.currentTitle ?? "",
imageUrl: articleImageUrl,
isFavorite: self.isFavorite)
delegate?.actionButtonDidPress(inside: currentArticle)
}
}
This is my NewsCell
elements hierarchy:
And these are the constraints I put on the action button inside the NewsCell
:
constraints on action button inside NewsCell
And this is how I constrained the ActionButton
UIView
:
constraints inside the action button custom uiview
In my TableView I initiate each cell like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentCell = tableView.dequeueReusableCell(withIdentifier: Constants.TableCellsIdentifier.FAVORITES, for: indexPath) as! SavedArticleCell
currentCell.delegate = self
let currentKey = viewModel.keysArray[indexPath.row]
let savedArticle = viewModel.savedArticles[currentKey]
if let savedArticle = savedArticle {
currentCell.articleID = savedArticle.id!
currentCell.articleTitle.text = savedArticle.title
currentCell.articleTopic.setTitle(savedArticle.topic, for: .normal)
if let imageUrl = savedArticle.imageUrl {
currentCell.articleImageURL = imageUrl
currentCell.articleImage.sd_setImage(with: URL(string: imageUrl), placeholderImage: UIImage(named: "light-gray-background"))
} else {
currentCell.articleImage.image = UIImage(named: "light-gray-background")
}
}
return currentCell
}
CodePudding user response:
You have to add contentView.layoutIfNeeded()
in layoutSubViews()
method of NewsCell
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0))
contentView.layoutIfNeeded()
}
The purpose of layoutIfNeeded()
is to force a synchronous (essentially "immediate") redraw of a view and its subviews. You have to call this because you have updated the layout in layoutsubviews()
method. Regarding layoutIfNeeded()
Here is what Apple says.
Use this method to force the view to update its layout immediately. When using Auto Layout, the layout engine updates the position of views as needed to satisfy changes in constraints. Using the view that receives the message as the root view, this method lays out the view subtree starting at the root. If no layout updates are pending, this method exits without modifying the layout or calling any layout-related callbacks.