Lo que quiero hacer (literalmente): mantener una celda de vista de tabla durante un período de tiempo específico. Una vez que alcanza ese período de tiempo, la altura de la celda aumenta gradualmente. Cuando suelto el dedo, la altura de la celda deja de crecer.

Lo que tengo: Tengo varias tableViewCells. Después de presionar en una celda durante un período de tiempo específico usando:

let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: Selector("longPress:"))
longPressRecognizer.minimumPressDuration = 1.0s
longPressRecognizer.delegate = self
self.view.addGestureRecognizer(longPressRecognizer)

Quería aumentar la altura de la celda. Pero no podía hacer eso sin saber en qué fila estaba ubicado el toque, así que llegué hasta aquí:

func longPress(longPressGestureRecognizer: UILongPressGestureRecognizer) {
    if longPressGestureRecognizer.state == UIGestureRecognizerState.began {
        let touchPoint = longPressGestureRecognizer.location(in: self.tableView)
        if let indexPath = tableView.indexPathForRow(at: touchPoint) {
            //let cell = tableView.cellForRow(at: indexPath)
        }
    }
}

Pensé en controlar rowHeight, pero eso está únicamente dentro de las funciones tableView, así que no sabía cómo llamarlo.

No estoy seguro de cómo proceder. Y no estoy buscando nada que tenga que ver con .beginUpdates y .endUpdates porque quiero que el crecimiento celular sea gradual y preferiblemente animado.

Cualquier ayuda sería muy apreciada, ya que he estado buscando respuestas a este problema específico durante bastante tiempo.

Código que incluye la declaración rowHeight:

     override func viewDidLoad() {
            super.viewDidLoad()

    //        let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: Selector("longPress:"))
    //        longPressRecognizer.minimumPressDuration = 1.0 // 1 second press
    //        longPressRecognizer.delegate = self
    //        self.view.addGestureRecognizer(longPressRecognizer)

            tableView.delegate = self
            tableView.dataSource = self
            tableView.allowsSelection = false

            self.tableView.reorder.delegate = self

            view.backgroundColor = UIColor(red:0.64, green:0.93, blue:0.78, alpha:1.0)
            tableView.backgroundColor = UIColor.clear

            tableView.rowHeight = 84
            tableView.rowHeight = UITableViewAutomaticDimension


            // Do any additional setup after loading the view.
        }

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
2
user6934210 14 feb. 2018 a las 12:53

2 respuestas

La mejor respuesta

No me gusta explicar las cosas en detalle usando palabras, así que en lugar de una respuesta larga, solo incluiré un código relativamente corto (disponible también como gist) que contiene comentarios que explican los conceptos básicos. Realmente no he prestado mucha atención a la arquitectura y al código limpio, solo me concentré en hacer la tarea. Tome el código como tal, intente mejorarlo en su base de código.

De todos modos, el código debería ser bastante claro y autoexplicativo, pero me gustaría esbozar un panorama general antes de sumergirse en él. En la solución, mantengo las alturas en una matriz de CGFloat (variable cellHeights) y modifico una altura para una fila dada cambiando la altura correspondiente en una matriz. Esa matriz sirve como base para la implementación de heightForRowAt.

Cuando comienza la pulsación larga, enciendo un temporizador, que cada 0,1 segundos actualiza la altura de la fila seleccionada modificando la altura en la matriz cellHeights y diciéndole al tableView que se vuelva a dibujar. Eso sucede hasta que se alcanza el límite para esa fila dada y luego simplemente cancelo (invalido) el temporizador.

Si la pulsación larga finaliza antes de que se alcance la altura límite, simplemente cancelo explícitamente el temporizador, de modo que la celda deja de agrandarse cuando el usuario suelta la prensa.

Y eso es. Le sugiero que tome el EnlargingCellsOnLongPressController gist (o el de abajo, es el mismo código), pruébelo en su propio dispositivo, lea el código junto con los comentarios y creo que debería poder implementarlo en su propia situación.

import UIKit

class EnlargingCellsOnLongPressController: UITableViewController {
    // this will hold the indexPath of the cell we are currently enlarging
    var enlargingIndexPath: IndexPath?
    // dummy data model
    var modelItems: [String] = []

    // we will need to keep the heights for each particular cell (since any can be resized)
    var cellHeights: [CGFloat] = []

    // some height limit, I will set it for myself to 200
    let limitHeight = CGFloat(200)

    // the enlarging itself will be done by a timer
    weak var timer: Timer?

    override func viewDidLoad() {
        super.viewDidLoad()
        // nothing special here
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.estimatedRowHeight = 100
        tableView.allowsSelection = false

        // creating some dummy data, 30 items, and for each a cell height that will start at 100
        for index in 0..<30 {
            modelItems.append("Item \(index)")
            // by default we will start with 100
            cellHeights.append(CGFloat(100))
        }

        // please, use swift 4 and the new #selector syntax
        let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPress(longPressGestureRecognizer:)))
        longPressRecognizer.minimumPressDuration = 1
        self.view.addGestureRecognizer(longPressRecognizer)
    }

    // following three methods should be clear
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return modelItems.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = modelItems[indexPath.row]
        return cell
    }

    // for height for row we will return a specific height from cellHeights array
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return cellHeights[indexPath.row]
    }

    @objc func longPress(longPressGestureRecognizer: UILongPressGestureRecognizer) {
        if longPressGestureRecognizer.state == UIGestureRecognizerState.began {
            let touchPoint = longPressGestureRecognizer.location(in: self.tableView)
            if let indexPath = tableView.indexPathForRow(at: touchPoint) {
                //when the press starts on a cell, we will keep the indexPath for the cell
                self.enlargingIndexPath = indexPath
                // and turn on enlarging
                self.startEnlarging()
            }
        } else if longPressGestureRecognizer.state == .ended {
            // when the press is ended, we can stop enlarging
            stopEnlarging()
        }
    }

    func startEnlarging() {
        // interval 0.1 second seems smooth enough (redraw seems to be animated anyway)
        timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { [weak self] (timer) in
            guard let strongSelf = self, let enlargingIndexPath = self?.enlargingIndexPath else { return }

            let oldHeight = strongSelf.cellHeights[enlargingIndexPath.row]
            // since the timer repeats every 0.1 second, this will enlarge the cell 20 points per second till limit
            // in one cycle I will enlarge the cell by two points
            let newHeight = oldHeight + 2
            if newHeight < strongSelf.limitHeight {
                // if the newHeight did not reach limit,
                // update height and redraw tableView
                strongSelf.cellHeights[enlargingIndexPath.row] = newHeight
                strongSelf.tableView.beginUpdates()
                strongSelf.tableView.setNeedsLayout()
                strongSelf.tableView.endUpdates()
            } else {
                // reached maximum size, just cancel the timer
                strongSelf.stopEnlarging()
            }
        })
    }

    func stopEnlarging() {
        // this just cancels the timer
        timer?.invalidate()
    }
}

ACTUALIZACIÓN

En aras de la exhaustividad, he creado un gist utilizando autolayout y UITableViewAutomaticDimension, si alguna vez decides usar eso en lugar de heightForRowAt. Pero el principio es el mismo.

0
Milan Nosáľ 16 feb. 2018 a las 15:27
Declare property for height of the cell and return this property in the tableviews's delegate method heightForRowAtIndexPath method

var heightForCell = 100

func longPress(longPressGestureRecognizer: UILongPressGestureRecognizer) {
if longPressGestureRecognizer.state == UIGestureRecognizerState.began {
    let touchPoint = longPressGestureRecognizer.location(in: self.tableView)
    if let indexPath = tableView.indexPathForRow(at: touchPoint) {

          self. heightForCell = // height you want to set 
          // now reload cell again
    }
} 
}
1
Punit 14 feb. 2018 a las 12:05