调用 reloadData() 后自行调整动态 UICollectionViewCell 高度的大小不正确
Posted
技术标签:
【中文标题】调用 reloadData() 后自行调整动态 UICollectionViewCell 高度的大小不正确【英文标题】:Self sizing dynamic UICollectionViewCell height improperly sizes itself after calling reloadData() 【发布时间】:2022-01-16 02:30:43 【问题描述】:我有一个 UICollectionView 可以自动调整单元格的高度,因此根据单元格内的文本量,它会适当地调整高度。
这工作得很好,我可以单击所有按钮,向上或向下滚动等,但问题是当我调用 reloadData() 时,collectionViewCell 的约束被搞砸了,它们出于某种原因相互堆叠.
这是调用reloadData()
之前collectionView的图片:
这是我打电话给reloadData()
后收集的照片:
任何人都可能知道为什么会发生这种情况以及我该如何解决?
这是我的 CustomCollectionView 代码:
class CustomCollectionView: UICollectionView
public let bottomRefresh = CollectionViewBottomRefresh()
init()
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
layout.scrollDirection = .vertical
layout.estimatedItemSize = CGSize(width: UIScreen.main.bounds.width, height: 50)
super.init(frame: .zero, collectionViewLayout: layout)
alwaysBounceVertical = true
backgroundColor = .systemBackground
delaysContentTouches = false
showsVerticalScrollIndicator = false
register(PostView.self, forCellWithReuseIdentifier: "post")
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func touchesShouldCancel(in view: UIView) -> Bool
if view is UIButton || view is UITextField
return true
return super.touchesShouldCancel(in: view)
这是我的 CollectionViewCell 代码:
class PostView: UICollectionViewCell,
override init(frame: CGRect)
super.init(frame: frame)
contentView.addSubview(commentsButton)
contentView.addSubview(kuduAppTeamDeleteButton)
contentView.addSubview(titleLabel)
contentView.addSubview(infoButton)
contentView.addSubview(imageViewButton)
contentView.addSubview(likeButton)
contentView.addSubview(followButton)
contentView.addSubview(profile)
contentView.addSubview(likeCount)
contentView.addSubview(date)
contentView.addSubview(line)
addConstraints()
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
public func setupView(post: Post)
guard let post = fb.posts.firstIndex(where: p in p.id == post.id ) else return
self.post = post
guard let user = fb.users.firstIndex(where: user in user.id == fb.posts[post].uid ) else return
self.user = user
if fb.currentUser.likes.contains(fb.posts[post].id)
self.likeButton.setImage(UIImage(systemName: "hand.thumbsup.fill"), for: .normal)
self.likeButton.tintColor = UIColor.theme.blueColor
else
self.likeButton.setImage(UIImage(systemName: "hand.thumbsup"), for: .normal)
self.likeButton.tintColor = .label
let result = String(format: "%ld %@", locale: Locale.current, fb.posts[post].likeCount, "")
likeCount.text = result
//Button Actions
infoButton.addAction(infoButtonAction, for: .touchUpInside)
likeButton.addAction(likeButtonAction, for: .touchUpInside)
followButton.addAction(followButtonAction, for: .touchUpInside)
imageViewButton.addAction(imageViewButtonAction, for: .touchUpInside)
profile.addAction(profileAction, for: .touchUpInside)
commentsButton.addAction(commentsButtonAction, for: .touchUpInside)
kuduAppTeamDeleteButton.addAction(kuduAppTeamDeleteButtonAction, for: .touchUpInside)
//Date
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .none
dateFormatter.dateStyle = .long
let dateString = dateFormatter.string(from: fb.posts[post].date)
date.text = dateString
//Set follow button text
if self.fb.currentUser.following.contains(fb.users[user].id)
self.followButton.label.text = "Unfollow"
else
self.followButton.label.text = "Follow"
//Set imageview image
imageViewButton.setImage(fb.posts[post].image, for: .normal)
imageViewButton.imageView!.contentMode = .scaleAspectFill
//Set user image
profile.usernameLabel.text = fb.users[user].username
profile.profileImage.image = fb.users[user].profileImage
if profile.profileImage.image == UIImage(systemName: "person.circle.fill")
profile.profileImage.tintColor = UIColor.theme.accentColor
//Set post title
titleLabel.text = fb.posts[post].title
override func prepareForReuse()
//Remove all actions
infoButton.removeAction(infoButtonAction, for: .touchUpInside)
likeButton.removeAction(likeButtonAction, for: .touchUpInside)
imageViewButton.removeAction(imageViewButtonAction, for: .touchUpInside)
profile.removeAction(profileAction, for: .touchUpInside)
commentsButton.removeAction(commentsButtonAction, for: .touchUpInside)
followButton.removeAction(followButtonAction, for: .touchUpInside)
kuduAppTeamDeleteButton.removeAction(kuduAppTeamDeleteButtonAction, for: .touchUpInside)
//Remove any other text or images
for subview in imageViewButton.subviews
if let subview = subview as? UIImageView, subview.image == UIImage(systemName: "play.circle.fill")
subview.removeFromSuperview()
imageViewButton.setImage(nil, for: .normal)
kuduAppTeamDeleteButton.color = nil
profile.profileImage.image = nil
titleLabel.text = nil
date.text = nil
self.followButton.label.text = nil
// Sets a requried width and a dynamic height that changes depending on what is in the cell. So we can have searchbar as first cell heigh 50, and post in other cells with height of view.bounds.width.
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes
let targetSize = CGSize(width: layoutAttributes.frame.width, height: 0)
layoutAttributes.frame.size = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
return layoutAttributes
private func addConstraints()
kuduAppTeamDeleteButton.height(30)
kuduAppTeamDeleteButton.width(UIScreen.main.bounds.width / 4)
kuduAppTeamDeleteButton.bottom(to: commentsButton)
kuduAppTeamDeleteButton.leftToRight(of: commentsButton, offset: 5)
imageViewButton.width(UIScreen.main.bounds.width)
imageViewButton.height(UIScreen.main.bounds.width * 9/16)
imageViewButton.topToSuperview()
infoButton.leftToRight(of: titleLabel, offset: 6)
infoButton.topToBottom(of: imageViewButton, offset: 15)
infoButton.width(30)
infoButton.height(30)
titleLabel.horizontalToSuperview(insets: UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 40))
titleLabel.topToBottom(of: imageViewButton, offset: 5)
titleLabel.height(min: 50)
likeButton.topToBottom(of: titleLabel, offset: 10)
likeButton.trailingToSuperview(offset: 10)
likeButton.height(32)
likeButton.width(32)
profile.leadingToSuperview(offset: 5)
profile.topToBottom(of: titleLabel, offset: 10)
profile.widthToSuperview(multiplier: 0.4)
likeCount.trailingToLeading(of: likeButton, offset: -5)
likeCount.topToBottom(of: titleLabel, offset: 15)
followButton.topToBottom(of: titleLabel, offset: 5)
followButton.trailingToLeading(of: likeCount, offset: -10)
followButton.height(50)
followButton.width(UIScreen.main.bounds.width / 4)
date.bottom(to: commentsButton, offset: -5)
date.trailingToSuperview(offset: 5)
commentsButton.topToBottom(of: profile, offset: 10)
commentsButton.leadingToSuperview(offset: 5)
line.horizontalToSuperview()
line.bottom(to: commentsButton)
line.height(1)
contentView.bottom(to: line)
contentView.widthToSuperview()
提前致谢!
【问题讨论】:
【参考方案1】:终于想通了,如果其他人遇到这个问题,你可以如何解决这个问题。
所以我决定使用 UITableView 而不是 UICollectionView。当您更改为 UITableView 时,您可以访问变量UITableView.automaticDimension
。
附注:只有一列才能使用 UITableView。
这就是我为 UITableView 所做的:
class MyTableView: UITableView
public let bottomRefresh = TableViewBottomRefresh()
init()
super.init(frame: .zero, style: .plain)
rowHeight = UITableView.automaticDimension
estimatedRowHeight = 500
separatorStyle = .none
allowsSelection = false
delaysContentTouches = false
alwaysBounceVertical = true
showsVerticalScrollIndicator = false
register(MyTableViewCell.self, forCellReuseIdentifier: "MyCell")
然后你需要创建一个containerView,以后可以将所有单元格内容放入其中:
private let containerView: UIView =
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
()
添加contentView.addsubview(containerView)
并将所有单元格内容放入containerView中,以便您的单元格可以自动调整大小:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(containerView)
containerView.addSubview(profileImage)
containerView.addSubview(username)
containerView.addSubview(editProfileButton)
addConstraints()
addconstraints()
函数:
private func addConstraints()
profileImage.topToSuperview()
profileImage.centerXToSuperview()
profileImage.widthToSuperview(multiplier: 1/2)
profileImage.height(UIScreen.main.bounds.width / 2)
username.topToBottom(of: profileImage, offset: 10)
username.horizontalToSuperview()
username.height(50)
editProfileButton.height(50)
editProfileButton.widthToSuperview(multiplier: 1/3)
editProfileButton.centerXToSuperview()
editProfileButton.topToBottom(of: username, offset: 10)
containerView.widthToSuperview()
containerView.bottom(to: editProfileButton)
希望这对某人有所帮助!如果有人知道如何使用动态 collectionViewCell 高度调用 reloadData()
而不会破坏单元格约束,请告诉我!我尝试了 2 周,但仍然没有找到解决方案:(。
【讨论】:
以上是关于调用 reloadData() 后自行调整动态 UICollectionViewCell 高度的大小不正确的主要内容,如果未能解决你的问题,请参考以下文章
具有自我调整大小的单元格的 UICollectionViewFlowLayout 在 reloadData 后崩溃
使用自我调整大小的单元格后,reloadData 不再起作用
UITableView, reloadData 重新加载单元格但不调整表格大小