简体   繁体   中英

How to increase the size of the tableview cell gradually upon holding it

What I want to do (verbatim): Hold a tableview cell for specified amount of time. Once it reaches that time period, the cell height increases gradually. When I release my finger, the cell height stops growing.

What I have: I have several tableViewCells. After pressing on a cell for a specified amount of time using:

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

I wanted to increase the cell height. But I couldn't do that without knowing at which row the touch was located, so I got up to here:

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)
        }
    }
}

I thought about controlling the rowHeight, but that's solely within the tableView functions so I did not know how to call it.

I am unsure how to proceed. And I am not looking for anything that has to do with .beginUpdates and .endUpdates because I want the cell growth to be gradual and preferably animated.

Any help would be greatly appreciated, as I have been searching for answers to this specific problem for quite a long time.

code that includes the rowHeight declaration:

     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
    }
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
    }
} 
}

I don't like to explain things in details using words, so instead of a long answer, I will just include a relatively short code (available also as gist ) that contains comments explaining the basics. I haven't really paid a lot of attention to architecture and clean code, I just focused on getting the task done. Take the code as such - try to make it better in your codebase.

Anyway, the code should be pretty clear and self-explanatory, but I would like to just sketch a big picture for you before you dive into it. In the solution, I keep the heights in an array of CGFloat (variable cellHeights ) and modify a height for a given row by changing the corresponding height in an array. That array serves as a basis for heightForRowAt implementation.

When long press begins, I start a timer, that every 0.1 seconds updates the height for the selected row by modifying the height in the cellHeights array and telling the tableView to redraw itself. That happens until the limit is reached for that given row and then I simply cancel (invalidate) the timer.

If the long press ends before the limit height is reached, I just explicitly cancel the timer, so that the cell stops enlarging when the user releases the press.

And that's it. I suggest you take the EnlargingCellsOnLongPressController gist (or the one below, it is the same code), try it on your own device, read the code along with the comments, and I believe you should be able to implement it in your own situation.

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()
    }
}

UPDATE

For the sake of completeness, I have create a gist using autolayout and UITableViewAutomaticDimension , if you ever decide to use that instead of heightForRowAt . But the principle is the same.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM