andrey andrey - 5 months ago 48
Swift Question

Shrink hight of collectionViewCell if no image

I have a cell with image pinned to left-top-right of cell. Other constraints related to image.

enter image description here

Here is image height constraint:

enter image description here

Here is my data setter:

import UIKit
import Kingfisher

class WorkoutSectionCollectionViewCell: UICollectionViewCell {

@IBOutlet weak var colorLine: UIView!
@IBOutlet weak var workoutName: UILabel!
@IBOutlet weak var widthConstraint: NSLayoutConstraint!
@IBOutlet weak var workoutDescription: UILabel!
@IBOutlet weak var parametersStackView: UIStackView!
@IBOutlet weak var exercisesIcon: UIImageView!
@IBOutlet weak var exercisesLabel: UILabel!
@IBOutlet weak var musclesIcon: UIImageView!
@IBOutlet weak var musclesLabel: UILabel!
@IBOutlet weak var workoutImage: UIImageView!

var workout: Workout? {
didSet {
// Get image form backend
if let imageUrl = workout?.workoutImage?.imageUrl {
let resource = ImageResource(downloadURL: URL(string: imageUrl)!, cacheKey: imageUrl)
workoutImage.kf.setImage(with: resource)
workoutImage.isHidden = false
} else {
workoutImage.isHidden = true
}

if let workoutKind = workout?.workoutKind {
switch workoutKind {
case "силовая": colorLine.backgroundColor = Colors.colorCadmiumOrange
case "фитнес": colorLine.backgroundColor = Colors.colorGreen
case "кардио": colorLine.backgroundColor = Colors.colorBlueDeFrance
case "HIIT": colorLine.backgroundColor = Colors.colorCarminePink
default:
colorLine.backgroundColor = Colors.colorClear
}
}

workoutName.text = workout?.workoutName

if let workoutDesc = workout?.workoutDesc {
workoutDescription.text = workoutDesc.html2String
}

if let numberOfExercises = workout?.workoutExercises?.count {
exercisesLabel.text = "\(numberOfExercises) " + pluralForm(number: numberOfExercises, forms: ["упражнение", "упражнения", "упражнений"])
}

var workoutMuscles: [String] = []
workout?.workoutExercises?.forEach({ (exercise) in
workoutMuscles.append((exercise as! Exercise).mainMuscle!)
})
musclesLabel.text = Set(workoutMuscles).joined(separator: ", ").lowercased()
}
}

override func awakeFromNib() {
super.awakeFromNib()

// Initialization code
self.contentView.translatesAutoresizingMaskIntoConstraints = false
let screenWidth = UIScreen.main.bounds.size.width
widthConstraint.constant = screenWidth - 20
self.layer.cornerRadius = 3
workoutName.textColor = Colors.colorPaynesGrey
workoutDescription.textColor = Colors.colorDimGray
exercisesLabel.textColor = Colors.colorSilver
musclesLabel.textColor = Colors.colorSilver
musclesIcon.tintColor = Colors.colorSilver
exercisesIcon.tintColor = Colors.colorSilver
}


I use KingFisher library to get image from backend by REST Api.

The problem is some cells have image and then everything is ok, but some cells haven't image and then empty space appear.

enter image description here

I would like to hide empty space if no image and shrink cell height to fit content. How I can do it?

Answer Source

If your image comes from an async server call, there its not a good idea to shrink the cell when there is no image in the call back. If you do so, your table view will glitch a lot and the user experience will be bad.

Instead, in your call to retrieve text in the first place, you need to check if there is image or not. Then, hide the image view on the first place instead of leaving the space at first and then check image existence.

To do this, in your server call, say you include a key saying hasImage in the returned JSON respond. We also need to set up the cell so that the image view on top has a height constraint. The text view on bottom needs to have constraint saying padding top. In this way, when you set your image view height to 0, your text view will automatically moves up to shrink a cell.

First, give your height constraint an identifier so that we can change it from code. It looks like this

enter image description here

Now, in your cellForRow, you can do something like

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "ImageCell") as! ImageCell
    if dataArray[indexPath.row].hasImage {
        //add image
    } else {
        let filteredConstraints = cell.imgView.constraints.filter { $0.identifier == "imgTxtCellImgHeight" }
        if let heightConstraint = filteredConstraints.first {
            heightConstraint.constant = 0
        }
    }
    return cell
}