首页 > 解决方案 > 在tableview单元格内创建动态collectionView

问题描述

我正在尝试在 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 大小。

标签: iosswiftuitableviewuicollectionviewuicollectionviewcell

解决方案


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

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

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

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


推荐阅读