自定义 UITableViewCell 的动态高度问题

Posted

技术标签:

【中文标题】自定义 UITableViewCell 的动态高度问题【英文标题】:Dynamic Height Issue for Custom UITableViewCell 【发布时间】:2020-12-16 16:22:26 【问题描述】:

我有一个自定义UITableViewCell,其中包含一个UICollectionView

我在其XIB 文件的每一侧都固定了UICollectionView

在我的一些单元格中,内容可能会向下移动,但根据我当前的动态高度设置,我只能看到顶部。我正在将图像添加到我的UICollectionView,所以在一个单元格中可能有 20 个,而另一个可能只有 5 个。现在每一行都有相同的高度,而有些应该不同。

请注意,单元格中的UICollectionView 不会滚动。

这是我在视图控制器中尝试的:

// Here is where I am getting the arr data, which is the folders
// that contains images.
func getDataForSections() 
    
    let storageReference = Storage.storage()
    let ref = storageReference.reference().child("abc/xyz/")
    
    ref.listAll  (result, error) in
        
        if let error = error 
            // ...
        
                    
        for prefix in result.prefixes 
            self.arr.append(prefix)
        
        
        self.myTableView.reloadData()
    


func numberOfSections(in tableView: UITableView) -> Int 
    
    return arr.count


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    
    return 1


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        
    let data = arr[indexPath.section]
    let cell = tableView.dequeueReusableCell(withIdentifier: "collectionCell", for: indexPath) as! collectionCell
    cell.getImagesFromData(data: data)
    cell.frame = tableView.bounds
    cell.layoutIfNeeded()
    cell.collectionView.reloadData()
    cell.collectionView.heightAnchor.constraint(equalToConstant: cell.collectionView.collectionViewLayout.collectionViewContentSize.height).isActive = true
    cell.layoutIfNeeded()
    return cell


func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat 

    // How Do I get the Custom size here? Or in heightForRowAt?
    return 500.0


func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        
    return UITableView.automaticDimension


/**
 @param numberOfCellsInCollectionView the number of cells of your current collectioview
 */
func calculateHeight(numberOfCellsInCollectionView: Int) -> CGFloat 
    let imageHeight: CGFloat = 40.0
    let spaceBetweenRows: CGFloat = 20.0 /*Change as you please based on the space you put between your cells (if any) */
    let rows: CGFloat = CGFloat(calculateRows(numberOfCellsInCollectionView: numberOfCellsInCollectionView))
    
    /**
     1) (rows*imageHeight) calculate the total space occupied by the pictures
     2) (rows+2) assuming you want to put a little space between the top and the bottom of the collectionview i used +2. If you don't want to remove the +2
     3) ((rows+2)*spaceBetweenRows) total space occupied by the spaces.
     **/
    let height = (rows*imageHeight)+((rows+2)*spaceBetweenRows)
   
    return height
    


//Calculate the rows
func calculateRows(numberOfCellsInCollectionView: Int) -> Int 
    let result = numberOfCellsInCollectionView/6 //Dividing the number of cells for the cells for row
    let rest = numberOfCellsInCollectionView%6 //Calculating the module
    
    if rest == 0 
        //If the rest is 0 (ie: you divide 18/6), then you get the result of the division (18/6 = 3)
        return result
     else 
        //If the rest is > 0 (ie: you divide 17/6), then you get the result of the division + 1 (17/6 = 3+1 = 4) so there's space for the last item
        return result+1
    

这是我在具有集合视图的自定义单元格中尝试的内容:

func getImagesFromData(prefix: String) 
    
    let storageReference = Storage.storage()
    let ref = storageReference.reference().child("abc/xyz/\(prefix)")
    
    ref.listAll  (result, error) in
                    
        if let error = error 
            // ...
        
        
        self.folderImages.removeAll()
                    
        for item in result.items 
            
            if !self.folderImages.contains(item) 
                self.folderImages.append(item)
            
        

       // Here is where I need to store (or retain) the count 
       // for each section and then calculate the height or pass
       // this data back to the View Controller. 
       // But with Firebase I am not sure how to return this or 
       // use a completion.
                                            
        // reload collection data
        self.myCollectionView.reloadData()
    


extension customCollectionCell: UICollectionViewDataSource, UICollectionViewDelegate 

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    
        return folderImages.count
    

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "imageCell", for: indexPath) as! imageCell
        let theImage = folderImages[indexPath.item]
        cell.imageView.sd_setImage(with: theImage, placeholderImage: UIImage(named: "blah"))  (image, error, cacheType, ref) in
            if error != nil 
                cell.imageView.image = UIImage(named: "blah")
            
        
        return cell
    

另一个例子:

这里不是使用UITableViewUICollectionViewCell,而是使用UICollectionViewUICollectionViewCell...

我还使用UIViewController 中单元格内的方法的变体来获取计数。

在这个例子中,我可以看到返回之前的值。我只需要知道如何在返回值中获取该值作为高度。

这是我尝试过的:

func collectionView(_ collectionView: UICollectionView,
                  layout collectionViewLayout: UICollectionViewLayout,
                  sizeForItemAt indexPath: IndexPath) -> CGSize 

    let prefix = arr[indexPath.section]

    getImageCountFromPrefix(prefix: prefix.name, completion:  (count, success) in
        if success 
            self.h = self.calculateHeight(numberOfCellsInCollectionView: count)
            // self.h has a value!!! How do I get it in the return?
        
    )

    return CGSize(width: collectionView.bounds.size.width, height: self.h)

【问题讨论】:

【参考方案1】:

创建一个函数来确定单元格内图片的高度,然后只使用heightForRowAtIndexPath 函数,如下所示:

 private func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
       /* Here I'll just assume the height of the picture is 30. You'll have to put here your function and return the dimension. */
        return 30.0
    

请注意,您可能希望在返回值中添加一个常量值,以使单元格的外观更加清晰。我一般加70.0

哦,我不知道图像,但是当您处理文本时,您还必须在 cellForRowAt 中调用这些。

cell.textLabel?.sizeToFit()
cell.textLabel?.numberOfLines = 0

这里是如何计算你的收藏视图的高度

/**
 @param numberOfCellsInCollectionView the number of cells of your current collectioview
 */
func calculateHeight(numberOfCellsInCollectionView: Int) -> CGFloat
    let imageHeight: CGFloat = 40.0
    let spaceBetweenRows: CGFloat = 5.0 /*Change as you please based on the space you put between your cells (if any) */
    let rows: CGFloat = CGFloat(calculateRows(numberOfCellsInCollectionView: numberOfCellsInCollectionView))
    
    /**
     1) (rows*imageHeight) calculate the total space occupied by the pictures
     2) (rows+2) assuming you want to put a little space between the top and the bottom of the collectionview i used +2. If you don't want to remove the +2
     3) ((rows+2)*spaceBetweenRows) total space occupied by the spaces.
     **/
    let height = (rows*imageHeight)+((rows+2)*spaceBetweenRows)
   
    return height
    


//Calculate the rows
func calculateRows(numberOfCellsInCollectionView: Int) -> Int
    let result = numberOfCellsInCollectionView/6 //Dividing the number of cells for the cells for row
    let rest = numberOfCellsInCollectionView%6 //Calculating the module
    
    if rest == 0 
        //If the rest is 0 (ie: you divide 18/6), then you get the result of the division (18/6 = 3)
        return result
     else 
        //If the rest is > 0 (ie: you divide 17/6), then you get the result of the division + 1 (17/6 = 3+1 = 4) so there's space for the last item
        return result+1
    

【讨论】:

图片为 40x40。 UICollectionView 已设置好,因此将连续出现 6 个。 对,我的错,我忽略了部分问题。但是,您应该计算集合视图的高度并将其返回到 heightForRowAtIndexPath 中。您还应该调用 cell.sizeToFit() 但我不知道 numberOfLines 的等价物。如果您需要更多帮助,请告诉我 关于计算 UICollectionView 高度的最佳方法有什么建议吗?所以现在我得到的值看起来是我需要的,但这是在其他所有内容都加载之后。所以不确定如何正确返回该值。 感谢您对此提供的帮助。所以,虽然这些确实返回了我需要的值,但我仍然不知道应该在哪里调用它们。在我可以将值传递给它之前,我的所有设置都已加载。我正在使用 Firebase。所以我想知道是不是因为这个。我似乎无法找到如何从他们的方法中返回值,并且我可以在任何调整大小的方法中使用它们。 在heightForRowAtIndexPath中调用计算高度,使其成为返回值。如果在加载视图之前无法访问数据,这不是问题。您可以创建辅助变量,您将在加载所需数据后为其赋值。使用这些辅助变量代替您丢失的数据。

以上是关于自定义 UITableViewCell 的动态高度问题的主要内容,如果未能解决你的问题,请参考以下文章

IOS:具有自定义 uitableviewcell 的动态高度

根据标签文本长度动态自定义 UITableViewCell 的高度

如何在swift中设置自定义UITableviewcell的动态高度

Swift中基于标签文本长度的动态自定义UITableViewCell的高度

在具有动态高度的 IB uitableviewcell 中使用带有 XIB 的自定义视图

iOS8中自定义UITableViewCell动态高度的内容有不同的宽度