table 加载时 TableView 自定义单元格 "jumps" 内的自定义 UIView
Custom UIView inside TableView Custom Cell "jumps" on table load
我有一个 UITableView
,其中包含名为 NewsCell
的自定义单元格。
在 NewsCell
中,我有一个自定义 UIView
用作操作按钮。
很多时候 TableView
正在加载单元格时,自定义操作按钮会出现在单元格的 顶部 几秒钟,直到它返回到它在单元格底部的正确位置。有时它 returns 只有在我将单元格滚动出视口后它才会到达正确的位置。
知道为什么会这样吗?
这是一张让事情更清楚的照片:
action button out of place
NewsCell 是使用 .xib 文件创建的,这里是相关代码:
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)
}
}
这是我的 NewsCell
元素层次结构:
news cell elements
这些是我对 NewsCell
:
中的操作按钮施加的约束
constraints on action button inside NewsCell
这就是我限制 ActionButton
UIView
的方式:
constraints inside the action button custom uiview
在我的 TableView 中,我像这样启动每个单元格:
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
}
您必须在 NewsCell
的 layoutSubViews()
方法中添加 contentView.layoutIfNeeded()
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0))
contentView.layoutIfNeeded()
}
layoutIfNeeded()
的目的是强制同步(本质上是“立即”)重绘视图及其子视图。您必须调用它,因为您已经在 layoutsubviews()
方法中更新了布局。关于 layoutIfNeeded()
这是 Apple 所说的。
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.
我有一个 UITableView
,其中包含名为 NewsCell
的自定义单元格。
在 NewsCell
中,我有一个自定义 UIView
用作操作按钮。
很多时候 TableView
正在加载单元格时,自定义操作按钮会出现在单元格的 顶部 几秒钟,直到它返回到它在单元格底部的正确位置。有时它 returns 只有在我将单元格滚动出视口后它才会到达正确的位置。
知道为什么会这样吗?
这是一张让事情更清楚的照片: action button out of place
NewsCell 是使用 .xib 文件创建的,这里是相关代码:
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)
}
}
这是我的 NewsCell
元素层次结构:
news cell elements
这些是我对 NewsCell
:
constraints on action button inside NewsCell
这就是我限制 ActionButton
UIView
的方式:
constraints inside the action button custom uiview
在我的 TableView 中,我像这样启动每个单元格:
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
}
您必须在 NewsCell
layoutSubViews()
方法中添加 contentView.layoutIfNeeded()
override func layoutSubviews() {
super.layoutSubviews()
contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0))
contentView.layoutIfNeeded()
}
layoutIfNeeded()
的目的是强制同步(本质上是“立即”)重绘视图及其子视图。您必须调用它,因为您已经在 layoutsubviews()
方法中更新了布局。关于 layoutIfNeeded()
这是 Apple 所说的。
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.