在tableview单元格内创建动态collectionView

Posted

技术标签:

【中文标题】在tableview单元格内创建动态collectionView【英文标题】:Create dynamic collectionView inside tableview cell 【发布时间】:2020-03-03 20:07:02 【问题描述】:

我正在尝试在 tableviewcell 内创建一个水平集合视图,其中包含一个动态大小的 uiimageview,这将是强制视图。

我创建了一个 UIView :

public class CardImage: UIView 
    
    private var imageView: UIImageView?
    private var titleLabel: UILabel?
    private var descriptionLabel: UILabel?
    private var subtitleLabel: UILabel?
    
    private var icon: UIImage?
    private var imageURL: String?
    
    private var screenPercentage: CGFloat = 1.0
    
    override public func layoutSubviews() 
        super.layoutSubviews()
        layer.cornerRadius = 4
    
    
    public override func awakeFromNib() 
        super.awakeFromNib()
        setup()
        layoutSubviews()
    
    
    public init() 
        super.init(frame: .zero)
        setup()
        layoutSubviews()
    

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
        setup()
        layoutSubviews()
    
    
    public func fill(dto: SomeDTO) 
        setupImage(icon: dto.image, imageUrl: dto.imageURL)
        descriptionLabel?.text = dto.description
        titleLabel?.text = dto.title
        subtitleLabel?.text = dto.subtitle
        screenPercentage = dto.screenPercentage
    
    
        private func setup() 
            isUserInteractionEnabled = true
            
            translatesAutoresizingMaskIntoConstraints = false
            
            backgroundColor = .red
            
            titleLabel = UILabel()
            titleLabel?.textColor = .white
            titleLabel?.numberOfLines = 1
            addSubview(titleLabel!)
            
            descriptionLabel = UILabel()
            descriptionLabel?.textColor = .white
            descriptionLabel?.numberOfLines = 2
            addSubview(descriptionLabel!)
            
            subtitleLabel = UILabel()
            subtitleLabel?.textColor = .white
            subtitleLabel?.numberOfLines = 1
            addSubview(subtitleLabel!)
            
            imageView = UIImageView(frame: .zero)
            imageView?.backgroundColor = .red
            addSubview(imageView!)
            
            setupConstraints()
        
        
        private func setupImage(icon: UIImage?, imageUrl: String?) 
            if let url = imageURL 
                return
            
            
            guard let image = icon else 
                return
            
            
            imageView?.image = image
            imageView?.contentMode = .scaleAspectFit
            
            setNeedsDisplay()
            setNeedsLayout()
        
        
        
        private func setupConstraints() 
            imageView?.translatesAutoresizingMaskIntoConstraints = false
            imageView?.topAnchor.constraint(equalTo: topAnchor).isActive = true
            imageView?.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
            imageView?.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
            
            let screenSize = UIScreen.main.bounds
            let computedWidth = screenSize.width * screenPercentage
            imageView?.widthAnchor.constraint(equalToConstant: computedWidth).isActive = true
            //the image should be 16:9
            imageView?.heightAnchor.constraint(equalTo: widthAnchor, multiplier: 9.0/16.0).isActive = true
            
            titleLabel?.translatesAutoresizingMaskIntoConstraints = false
            titleLabel?.topAnchor.constraint(equalTo: imageView!.bottomAnchor, constant: 16).isActive = true
            titleLabel?.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16).isActive = true
            titleLabel?.heightAnchor.constraint(equalToConstant: 18).isActive = true
            
            subtitleLabel?.translatesAutoresizingMaskIntoConstraints = false
            subtitleLabel?.topAnchor.constraint(equalTo: titleLabel!.topAnchor).isActive = true
            subtitleLabel?.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16).isActive = true
            
            titleLabel?.widthAnchor.constraint(equalTo: subtitleLabel!.widthAnchor).isActive = true
            titleLabel?.trailingAnchor.constraint(equalTo: subtitleLabel!.leadingAnchor, constant: 6).isActive = true
            
            descriptionLabel?.translatesAutoresizingMaskIntoConstraints = false
            descriptionLabel?.topAnchor.constraint(equalTo: titleLabel!.bottomAnchor, constant: 16).isActive = true
            descriptionLabel?.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16).isActive = true
            descriptionLabel?.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16).isActive = true
            descriptionLabel?.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16).isActive = true
        

将在 CollectionViewCell 内:

public class CardImageCollectionViewCell: UICollectionViewCell 
    private let view: CardImage = CardImage()
    
    override public func awakeFromNib() 
        super.awakeFromNib()
        
        translatesAutoresizingMaskIntoConstraints = false
//this only pin the view to the four anchors of the uicollectionview
        view.pinToBounds(of: self.contentView)
        backgroundColor = .clear
        
        AccessibilityManager.setup(self, log: true)
    
    
    public func fill(dto: SomeDTO) 
        view.fill(dto: dto)
    

然后是 CollectionViewCell,在一个 tableviewcell 内,它有一个 collectionview:

public class CardImageCollectionView: UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout 
    
        @IBOutlet private weak var collectionView: UICollectionView!
        private var dto: [SomeDTO] = []
        
        public override func awakeFromNib() 
            super.awakeFromNib()
            setup()
        
        
        private func setup() 
            backgroundColor = .blue
            
            collectionView.backgroundColor = .clear
            collectionView.delegate = self
            collectionView.dataSource = self
            
            if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout 
                flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
            
            
            registerCells()
        
        
        func fill(dto: [SomaImageCardDTO]) 
            self.dto = dto
            DispatchQueue.main.async 
                self.collectionView.reloadData()
                self.collectionView.layoutIfNeeded()
            
        
        
        private func registerCells() 
            collectionView.register(UINib(nibName: String(describing: CardImageCollectionViewCell.self), bundle: nil), forCellWithReuseIdentifier: String(describing: CardImageCollectionViewCell.self))
        
        
        public func numberOfSections(in collectionView: UICollectionView) -> Int 
            return 1
        
        
        public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
            return dto.count
        
        //this should not be setted since the sizing is automatic 
    //    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    //        return CGSize(width: 304, height: 238)
    //    
    //
        public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
            
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: CardImageCollectionViewCell.self), for: indexPath) as! CardImageCollectionViewCell
            
            cell.fill(dto: dto[indexPath.row])
            return cell
        
    

我面临的两个问题是,图像不能假定任何大小,因为集合视图在填充一些信息之前没有任何大小。

然后,即使我设置了图像大小,我也无法将信息传递给 UITableViewcell 大小。

【问题讨论】:

【参考方案1】:

如果我正确理解您的问题,您在 UITableView 中有不止一行具有水平 UICollectionViews。这些集合视图的单元格根据其中的图像具有动态大小。

所以 UITableView 的宽度是固定的,但是行的高度取决于 UICollectionView 的高度,对吗?

我建议在 UITableView 中使用自定大小的单元格。请参阅此处的参考: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSelf-SizingTableViewCells.html

之后,你需要找到一种方法来正确计算 UICollectionView 的高度,这样 UITableView 单元格才能确定正确的高度。有几种方法可以做到这一点,例如通过覆盖 UICollectionView 的 intrinsicContentSize 属性并返回图像的最大高度。

【讨论】:

以上是关于在tableview单元格内创建动态collectionView的主要内容,如果未能解决你的问题,请参考以下文章

在单元格内编辑textview时,将另一个单元格添加到tableview

完成在单元格内编辑 textview 后向 tableview 添加另一个单元格

UITableView 上的水平滚动正在改变 UIPageControl 的位置,该 UIPageControl 附加到 TableView 单元格内的滚动视图

UItableview 单元格的动态高度与 UItableview 单元格内的 UItableview

tableView 单元格内的多个 collectionView 单元格在滚动时更改设计

TableView单元格内的TableView,自动调整单元格高度