点击展开UITableViewCell(自定义)-自动调整单元格大小-一些问题



在我的项目(UIKit,编程UI(中,我有一个带有部分的UITableView。单元格使用自定义类。加载时,所有单元格只显示3行信息(2个标签(。点击时,将显示所有内容。因此,我已经将我的自定义单元格类设置为具有两个容器,一个用于3行预览,另一个用于完整内容。当用户通过调用自定义单元格类上的方法(toggleFullView(点击单元格时,这些容器会在需要时从单元格的内容视图中添加/删除。此方法从didSelectRowAt:中的视图控制器调用

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let annotation = annotationsController.getAnnotationFor(indexPath)
//Expandable cell
guard let cell = tableView.cellForRow(at: indexPath) as? AnnotationCell else { return }
cell.toggleFullView()
tableView.reloadRows(at: [indexPath], with: .none)
//        tableView.reloadData()
}

基本上它是有效的,但也有一些问题:

  1. 我必须双击细胞使其膨胀,然后再次使其塌陷。第一次点击将执行tableView.reloadRows(位于:[indexPath],带:.none(的行动画,第二次点击将进行展开。如果我用tableView.reloadData((代替reloadRows,那么一次点击后就会发生扩展和折叠!但很明显,这会禁用任何动画,它只是捕捉到位。如何让它只需轻轻一按就可以工作?

  2. 当细胞膨胀时,其他一些随机细胞也会膨胀。我想这与可重复使用的细胞有关,但我一直无法补救。请参阅随附的视频(https://www.youtube.com/watch?v=rOkuqMnArEU)。

  3. 我想成为一个膨胀的细胞,一旦我点击另一个细胞膨胀,就会崩溃,我该如何看待这一点?

我的自定义单元格类:

import UIKit
class AnnotationCell: UITableViewCell, SelfConfiguringAnnotationCell {
//MARK: - Properties
private let titleLabelPreview = ProjectTitleLabel(withTextAlignment: .left, andFont: UIFont.preferredFont(forTextStyle: .headline))
private let titleLabelDetails = ProjectTitleLabel(withTextAlignment: .left, andFont: UIFont.preferredFont(forTextStyle: .headline))
private let detailsLabelShort = ProjectTitleLabel(withTextAlignment: .left, andFont: UIFont.preferredFont(forTextStyle: .subheadline), numberOfLines: 2)
private let detailsLabelLong = ProjectTitleLabel(withTextAlignment: .left, andFont: UIFont.preferredFont(forTextStyle: .subheadline), numberOfLines: 0)
private let mapImageLabel = ProjectTitleLabel(withTextAlignment: .center, andFont: UIFont.preferredFont(forTextStyle: .footnote), andColor: .tertiarySystemGroupedBackground)
private let lastEditedLabel = ProjectTitleLabel(withTextAlignment: .center, andFont: UIFont.preferredFont(forTextStyle: .footnote), andColor: .tertiarySystemGroupedBackground)
private let checkmarkImageView = UIImageView()

private var checkmarkView = UIView()
private var previewDetailsView = UIStackView()
private var fullDetailsView = UIStackView()

private var showFullDetails = false

//MARK: - Init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

configureContents()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func layoutIfNeeded() {
super.layoutIfNeeded()

let padding: CGFloat = 5

if contentView.subviews.contains(previewDetailsView) {
//Constrain the preview view
NSLayoutConstraint.activate([
previewDetailsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
previewDetailsView.leadingAnchor.constraint(equalTo: checkmarkView.trailingAnchor, constant: padding),
previewDetailsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -2 * padding),
previewDetailsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding)
])
} else {
//Constrain the full view
NSLayoutConstraint.activate([
fullDetailsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
fullDetailsView.leadingAnchor.constraint(equalTo: checkmarkView.trailingAnchor, constant: padding),
fullDetailsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -2 * padding),
fullDetailsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding)
])
}
}

//MARK: - Actions
///Expand and collapse the cell
func toggleFullView() {
showFullDetails.toggle()

if showFullDetails {
//show the full version
if contentView.subviews.contains(previewDetailsView) {
previewDetailsView.removeFromSuperview()
}
if !contentView.subviews.contains(fullDetailsView) {
contentView.addSubview(fullDetailsView)
}
} else {
//show the preview version
if contentView.subviews.contains(fullDetailsView) {
fullDetailsView.removeFromSuperview()
}
if !contentView.subviews.contains(previewDetailsView) {
contentView.addSubview(previewDetailsView)
}
}
UIView.animate(withDuration: 1.2) {
self.layoutIfNeeded()
}
}

//MARK: - Layout
private func configureContents() {
backgroundColor = .clear
separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
selectionStyle = .none

detailsLabelShort.adjustsFontSizeToFitWidth = false
detailsLabelLong.adjustsFontSizeToFitWidth = false

checkmarkView.translatesAutoresizingMaskIntoConstraints = false
checkmarkView.addSubview(checkmarkImageView)

checkmarkImageView.tintColor = .systemOrange
checkmarkImageView.translatesAutoresizingMaskIntoConstraints = false

previewDetailsView = UIStackView(arrangedSubviews: [titleLabelPreview, detailsLabelShort])
previewDetailsView.axis = .vertical
previewDetailsView.translatesAutoresizingMaskIntoConstraints = false
previewDetailsView.addBackground(.blue)

fullDetailsView = UIStackView(arrangedSubviews: [titleLabelDetails, detailsLabelLong, mapImageLabel, lastEditedLabel])
fullDetailsView.axis = .vertical
fullDetailsView.translatesAutoresizingMaskIntoConstraints = false
fullDetailsView.addBackground(.green)

//By default only add the preview View
contentView.addSubviews(checkmarkView, previewDetailsView)
let padding: CGFloat = 5

NSLayoutConstraint.activate([
//Constrain the checkmark image view to the top left with a fixed height and width
checkmarkImageView.widthAnchor.constraint(equalToConstant: 24),
checkmarkImageView.heightAnchor.constraint(equalTo: checkmarkImageView.widthAnchor),
checkmarkImageView.centerYAnchor.constraint(equalTo: checkmarkView.centerYAnchor),
checkmarkImageView.centerXAnchor.constraint(equalTo: checkmarkView.centerXAnchor),
checkmarkView.widthAnchor.constraint(equalToConstant: 30),
checkmarkView.heightAnchor.constraint(equalTo: checkmarkView.widthAnchor),
checkmarkView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
checkmarkView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding)

])
self.layoutIfNeeded()
}

//MARK: - Configure cell with data
func configure(with annotation: AnnotationsController.Annotation) {
titleLabelPreview.text = annotation.title
titleLabelDetails.text = annotation.title
detailsLabelShort.text = annotation.details
detailsLabelLong.text = annotation.details
checkmarkImageView.image = annotation.complete ? ProjectImages.Annotation.checkmark : nil
lastEditedLabel.text = annotation.lastEdited.customMediumToString
mapImageLabel.text = annotation.mapImage?.title ?? "No map image attached"
}
}

好的,修复了它,一个完全扩展的表视图单元格。关键是使自定义单元格类中的布局无效,并在tableView上调用beginUpdates((和endUpdates(!

在我的视图中控制器:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Expandable cell
guard let cell = tableView.cellForRow(at: indexPath) as? AnnotationCell else { return }
cell.toggleFullView()
tableView.beginUpdates()
tableView.endUpdates()
}

和我的自定义单元格类toggleFullView((方法:

class AnnotationCell: UITableViewCell, SelfConfiguringAnnotationCell {
//MARK: - Properties
private let titleLabelPreview = ProjectTitleLabel(withTextAlignment: .left, andFont: UIFont.preferredFont(forTextStyle: .headline))
private let titleLabelDetails = ProjectTitleLabel(withTextAlignment: .left, andFont: UIFont.preferredFont(forTextStyle: .headline))
private let detailsLabelShort = ProjectTitleLabel(withTextAlignment: .left, andFont: UIFont.preferredFont(forTextStyle: .subheadline), numberOfLines: 2)
private let detailsLabelLong = ProjectTitleLabel(withTextAlignment: .left, andFont: UIFont.preferredFont(forTextStyle: .subheadline), numberOfLines: 0)
private let mapImageLabel = ProjectTitleLabel(withTextAlignment: .center, andFont: UIFont.preferredFont(forTextStyle: .footnote), andColor: .tertiarySystemGroupedBackground)
private let lastEditedLabel = ProjectTitleLabel(withTextAlignment: .center, andFont: UIFont.preferredFont(forTextStyle: .footnote), andColor: .tertiarySystemGroupedBackground)
private let checkmarkImageView = UIImageView()

private var checkmarkView = UIView()
private var previewDetailsView = UIStackView()
private var fullDetailsView = UIStackView()

let padding: CGFloat = 5

var showFullDetails = false

//MARK: - Init
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

configureContents()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

//MARK: - Actions
///Expand and collapse the cell
func toggleFullView() {
//Show the full contents
print("ShowFullDetails = (showFullDetails.description.uppercased())")
if showFullDetails {
print("Show full contents")
if contentView.subviews.contains(previewDetailsView) {
previewDetailsView.removeFromSuperview()
}
if !contentView.subviews.contains(fullDetailsView) {
contentView.addSubview(fullDetailsView)
}
NSLayoutConstraint.activate([
fullDetailsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
fullDetailsView.leadingAnchor.constraint(equalTo: checkmarkView.trailingAnchor, constant: padding),
fullDetailsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -2 * padding),
fullDetailsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding)
])
//Show preview contents
} else {
print("Show preview contents")
if contentView.subviews.contains(fullDetailsView) {
fullDetailsView.removeFromSuperview()
}
if !contentView.subviews.contains(previewDetailsView) {
contentView.addSubview(previewDetailsView)
}
NSLayoutConstraint.activate([
previewDetailsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
previewDetailsView.leadingAnchor.constraint(equalTo: checkmarkView.trailingAnchor, constant: padding),
previewDetailsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -2 * padding),
previewDetailsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}

showFullDetails.toggle()
//Invalidate current layout &
self.setNeedsLayout()
}

override func prepareForReuse() {
//Make sure reused cells start in the preview mode!
//        showFullDetails = false
}

override func layoutIfNeeded() {
super.layoutIfNeeded()

NSLayoutConstraint.activate([
//Constrain the checkmark image view to the top left with a fixed height and width
checkmarkImageView.widthAnchor.constraint(equalToConstant: 24),
checkmarkImageView.heightAnchor.constraint(equalTo: checkmarkImageView.widthAnchor),
checkmarkImageView.centerYAnchor.constraint(equalTo: checkmarkView.centerYAnchor),
checkmarkImageView.centerXAnchor.constraint(equalTo: checkmarkView.centerXAnchor),

checkmarkView.widthAnchor.constraint(equalToConstant: 30),
checkmarkView.heightAnchor.constraint(equalTo: checkmarkView.widthAnchor),
checkmarkView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
checkmarkView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding)
])
}

//MARK: - Layout
private func configureContents() {
//Setup Views
backgroundColor = .clear
separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
selectionStyle = .none

detailsLabelShort.adjustsFontSizeToFitWidth = false
detailsLabelLong.adjustsFontSizeToFitWidth = false

checkmarkView.translatesAutoresizingMaskIntoConstraints = false
checkmarkView.addSubview(checkmarkImageView)

checkmarkImageView.tintColor = .systemOrange
checkmarkImageView.translatesAutoresizingMaskIntoConstraints = false

previewDetailsView = UIStackView(arrangedSubviews: [titleLabelPreview, detailsLabelShort])
previewDetailsView.axis = .vertical
previewDetailsView.translatesAutoresizingMaskIntoConstraints = false
previewDetailsView.addBackground(.blue)

fullDetailsView = UIStackView(arrangedSubviews: [titleLabelDetails, detailsLabelLong, mapImageLabel, lastEditedLabel])
fullDetailsView.axis = .vertical
fullDetailsView.translatesAutoresizingMaskIntoConstraints = false
fullDetailsView.addBackground(.green)

//By default only show the preview View
contentView.addSubviews(checkmarkView)

//Setup preview/DetailView
toggleFullView()
}

//MARK: - Configure cell with data
func configure(with annotation: AnnotationsController.Annotation) {
titleLabelPreview.text = annotation.title
titleLabelDetails.text = annotation.title
detailsLabelShort.text = annotation.details
detailsLabelLong.text = annotation.details
checkmarkImageView.image = annotation.complete ? ProjectImages.Annotation.checkmark : nil
lastEditedLabel.text = annotation.lastEdited.customMediumToString
mapImageLabel.text = annotation.mapImage?.title ?? "No map image attached"
}
}

啊!

最新更新