【问题标题】:Expand UITableViewCell (custom) on tap - autosize cell - some issues点击展开 UITableViewCell(自定义) - 自动调整单元格 - 一些问题
【发布时间】:2020-11-10 01:24:31
【问题描述】:

在我的项目(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(at: [indexPath], with: .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"
    }
}

【问题讨论】:

    标签: swift uitableview uikit swift5 expand


    【解决方案1】:

    好的,解决了,一个完全扩展的 tableview 单元格。关键是使自定义单元格类中的布局无效并在 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"
        }
    }
    

    HTH!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多