Saltar al contenido

Swift: dificultad con imágenes de diferentes tamaños en TableViewCell

Solución:

Solución de trabajo 100% con código de muestra

Me las arreglé para lograr el mismo diseño con contenido de etiqueta dinámica y dimensiones de imagen dinámicas. Lo hice a través de limitaciones y Diseño automático. Eche un vistazo al proyecto de demostración en este Repositorio de GitHub


Como señaló Matt, tenemos que calcular la altura de cada celda después de descargar la imagen (cuando conocemos su ancho y alto). Tenga en cuenta que la altura de cada celda se calcula mediante el método delegado de tableView heightForRowAt IndexPath

Entonces, después de descargar cada imagen, guarde la imagen en una matriz en este indexPath y vuelva a cargar ese indexPath para que la altura se calcule nuevamente, según las dimensiones de la imagen.

Algunos puntos clave a tener en cuenta son los siguientes

  • Utilice 3 tipos de células. Uno para etiqueta, uno para subtítulo y uno para imagen. Dentro cellForRowAt inicializar y devolver la celda apropiada. Cada celda tiene un cellIdentifier pero la clase es la misma
  • número de secciones en tableView == recuento de la fuente de datos
  • número de filas en la sección == 3
    • La primera fila corresponde al título, la segunda fila corresponde al subtítulo y la tercera corresponde a la imagen.
  • el número de líneas para las etiquetas debe ser 0 para que la altura se calcule en función del contenido
  • Dentro cellForRowAt descargue la imagen de forma asincrónica, almacénela en una matriz y vuelva a cargar esa fila.
  • Al recargar la fila, heightForRowAt se llama, calcula la altura de celda requerida en función de las dimensiones de la imagen y devuelve la altura.
  • Entonces, la altura de cada celda se calcula dinámicamente en función de las dimensiones de la imagen

Eche un vistazo a algunos códigos

override func numberOfSections(in tableView: UITableView) -> Int {
  return arrayListItems.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  //Title, SubTitle, and Image
  return 3
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

switch indexPath.row {
case 0:
  //configure and return Title Cell. See code in Github Repo 

case 1:

  //configure and return SubTitle Cell. See code in Github Repo

case 2:

  let cellImage = tableView.dequeueReusableCell(withIdentifier: cellIdentifierImage) as! TableViewCell
  let item = arrayListItems[indexPath.section]
  //if we already have the image, just show
  if let image = arrayListItems[indexPath.section].image {
    cellImage.imageViewPicture.image = image
  }else {

    if let url = URL.init(string: item.imageUrlStr) {

      cellImage.imageViewPicture.kf.setImage(with: url) { [weak self] result in
        guard let strongSelf = self else { return } //arc
        switch result {
        case .success(let value):

          print("=====Image Size (value.image.size)"  )
          //store image in array so that `heightForRowAt` can use image width and height to calculate cell height
          strongSelf.arrayListItems[indexPath.section].image = value.image
          DispatchQueue.main.async {
          //reload this row so that `heightForRowAt` runs again and calculates height of cell based on image height
            self?.tableView.reloadRows(at: [indexPath], with: .automatic)
          }

        case .failure(let error):
          print(error) // The error happens
        }
      }

    }

  }


  return cellImage

default:
  print("this should not be called")
}

//this should not be executed
return .init()
}


override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
//calculate the height of label cells automatically in each section
if indexPath.row == 0 || indexPath.row == 1 { return UITableView.automaticDimension }

// calculating the height of image for indexPath
else if indexPath.row == 2, let image = arrayListItems[indexPath.section].image {

  print("heightForRowAt indexPath : (indexPath)")
  //image

  let imageWidth = image.size.width
  let imageHeight = image.size.height

  guard imageWidth > 0 && imageHeight > 0 else { return UITableView.automaticDimension }

  //images always be the full width of the screen
  let requiredWidth = tableView.frame.width

  let widthRatio = requiredWidth / imageWidth

  let requiredHeight = imageHeight * widthRatio

  print("returned height (requiredHeight) at indexPath: (indexPath)")
  return requiredHeight


}
else { return UITableView.automaticDimension }
}

Relacionado.

Otro enfoque que podemos seguir es devolver las dimensiones de la imagen de la solicitud de API. Si eso se puede hacer, simplificará mucho las cosas. Eche un vistazo a esta pregunta similar (para collectionView).

Celdas de vista de colección de tamaño propio con descarga de imágenes asíncronas.

Placholder.com Se utiliza para obtener imágenes de forma asincrónica

Células de tamaño propio: (una buena lectura)

Muestra

Muestra

Es relativamente fácil hacer lo que está describiendo: la vista de su imagen necesita una restricción de ancho que sea igual al ancho de la “pantalla” (como usted lo expresa) y una restricción de altura que sea proporcional a la restricción de ancho (multiplier) según las proporciones de la imagen descargada (también conocida como “relación de aspecto”). Este valor no se puede establecer de antemano; necesitas configurarlo una vez tengas la imagen descargada, ya que no conoces sus proporciones hasta entonces. Por lo tanto, necesita una salida a la restricción de altura para poder quitarla y reemplazarla con una que tenga la multiplier cuando lo sepas. Si sus otras restricciones son correctas en relación con la parte superior e inferior de la vista de imagen, todo lo demás seguirá como se desee.

Estas capturas de pantalla muestran que este enfoque funciona:

ingrese la descripción de la imagen aquí

(Desplazándose hacia abajo en la vista de tabla 🙂

ingrese la descripción de la imagen aquí

No es 100% idéntica a la interfaz deseada, pero la idea es la misma. En cada celda tenemos dos etiquetas y una imagen, y las imágenes pueden tener diferentes relaciones de aspecto, pero esas relaciones de aspecto se muestran correctamente, y las propias celdas tienen diferentes alturas dependiendo de eso.

Este es el código clave que utilicé:

    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Cell
    // in real life you’d set the labels here too
    // in real life you’d be fetching the image from the network...
    // ...and probably supplying it asynchronously later
    let im = UIImage(named:self.pix[indexPath.row])!
    cell.iv.image = im
    let con = cell.heightConstraint!
    con.isActive = false
    let ratio = im.size.width/im.size.height
    let newcon = NSLayoutConstraint(item: con.firstItem, attribute: con.firstAttribute, relatedBy: con.relation, toItem: con.secondItem, attribute: con.secondAttribute, multiplier: ratio, constant: 0)
    newcon.isActive = true
    cell.heightConstraint = newcon
    return cell

Existe una solución sencilla para su problema si no desea cambiar su diseño.

1- define tu celda

2- poner el UIImageView y otros elementos de la interfaz de usuario que le gusten dentro de su celda y agregue estas restricciones para la vista de la imagen: -top, inicial, final, inferior a supervista -restricciones de altura y agregue salida a su código (por ejemplo: heightConstraint)

ingrese la descripción de la imagen aquí

3-Cambie el relleno de contenido a: ajuste de aspecto

4- Carga tus imágenes a través de Kingfisher o de cualquier otra forma que desees, una vez que pases tu imagen, verifica el tamaño y calcula la relación: imageAspectRatio = height / width

5-Establezca heightConstraint.constant = screenWidth * imageAspectRatio

¡LayoutIfNeeded () de 6 llamadas para la celda y debería estar bien!

* Esta solución funciona con cualquier composición de diseño de interfaz de usuario, incluidas las vistas de pila, el punto es tener una restricción en las imágenes y permitir que la vista de tabla descubra cómo calcular y dibujar restricciones.

class CustomTableViewCell: UITableViewCell {

    @IBOutlet weak var heightConstraint: NSLayoutConstraint!
    @IBOutlet weak var sampleImageView: UIImageView!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    func configure(image:UIImage) {
        let hRatio = image.size.height / image.size.width
        let newImageHeight = hRatio * UIScreen.main.bounds.width
        heightConstraint.constant = newImageHeight
        sampleImageView.image = image
        sampleImageView.layoutIfNeeded()
    }
}

Resultado:
ingrese la descripción de la imagen aquí

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *