自调整动态 UICollectionViewCell 高度在调用 reloadData() 后不正确调整自身大小

Self sizing dynamic UICollectionViewCell height improperly sizes itself after calling reloadData()

我有一个自动调整单元格高度的 UICollectionView,因此它会根据单元格内的文本量适当调整高度。

这工作得很好我可以点击所有按钮,向上或向下滚动等,但问题是当我调用 reloadData() 时,collectionViewCell 的约束被搞砸了,它们堆叠在一起一些原因。

这里是调用reloadData()之前collectionView的图片:

这是我调用reloadData()后collectionVIew的图片:

有人可能知道为什么会这样,我该如何解决?

这是我的 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()
}

}

提前致谢!

终于想出了如果其他人遇到此问题如何解决此问题。

所以我决定使用 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 周,但仍然没有找到解决方案:(.