How to set the height of a cell depending on a UILabel with a typewriter effect

被刻印的时光 ゝ 提交于 2019-12-12 11:03:00

问题


I have a UILabel in a cell of UITableView.

To adjust the height of the cell depending the height of the label, it's ok, it works perfectly.

But I need to add another constraints. I need to display the UILabel with a typewriter effect (letter by letter).

My extension for the effect works well:

extension UILabel{

    func setTextWithTypeAnimation(id:String, typedText: String, pauseCharacterArray: [Int:Double], characterInterval: TimeInterval = 0.06 ) {

        text = ""

        let group = DispatchGroup()
        group.enter()

        DispatchQueue.global(qos: .userInteractive).async {

            for (index, character) in typedText.characters.enumerated() {

                DispatchQueue.main.async {
                    self.text = self.text! + String(character)
                }

                Thread.sleep(forTimeInterval: characterInterval)
            }

        group.leave()
    }

    group.notify(queue: .main) {
        //do something
    }
}

I tried to call this function in my configureCell fund :

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

    let cell = tableView.dequeueReusableCell(withIdentifier: "ParagraphTableViewCell", for: indexPath) as! ParagraphTableViewCell

    cell.delegate = self
    self.configureCell(cell: cell, atIndexPath: indexPath)
    return cell
 }


func configureCell(cell: ParagraphTableViewCell, atIndexPath indexPath: IndexPath) {

    let paragraph = paragraphArray[indexPath.row] as! Paragraph

    let pauseCharactersArray:[Int:Double] = [1:0, 6:0]
    cell.dialogueLabel.setTextWithTypeAnimation(id:"intro", typedText: "lorem ipsum", pauseCharacterArray: pauseCharactersArray)
}

But the label doesn't appear. I think it's because the height of the label in the cell is set to 0, and it's never updated.

I don't know how to adjust the height of the cell "in live" (every time a character is displayed)

EDIT

I use autolayout

EDIT 2

The typewriter effect, run in a simple UILabel without UITableView:

The cell set in the xib:

When I run, the UILabel doesn't appear:


回答1:


I was able to achieve using a different method.

  • I created a stack view to group the label and the buttons (and a sub stack view to line up your buttons).
  • On button's stack view, I pinned the height through IB.
  • On parent stack view. I pinned the stack view to cell's margin.
  • On label, I pinned both sides to fit the stack view (do not pin the bottom side, you are free to pin the top side)

Here is my IB Screen Shot to use as reference.

  • On your ViewController, set the following properties:

    yourTableView.rowHeight = UITableViewAutomaticDimension
    yourTableView.rowHeight.estimatedRowHeight = 40 // You should set an initial estimated row height here, the number 40 was chosen arbitrarily
    
  • I edited your method to add an callback.

    func setTextWithTypeAnimation(id:String, typedText: String, pauseCharacterArray: [Int:Double], characterInterval: TimeInterval = 0.06, callBackAfterCharacterInsertion:(()->())?) {
    
      text = ""
    
      let group = DispatchGroup()
      group.enter()
    
      DispatchQueue.global(qos: .userInteractive).async {
    
        for (_, character) in typedText.characters.enumerated() {
    
            DispatchQueue.main.async {
                self.text = self.text! + String(character)
                callBackAfterCharacterInsertion?()
            }
    
            Thread.sleep(forTimeInterval: characterInterval)
        }
    
        group.leave()
      }
    
      group.notify(queue: .main) {
        //do something
      }
    }
    
  • And through the callback, I called the beginUpdates() and endUpdates(), from the TableView, after each character update.

Hope this help you in any way.




回答2:


I found a solution but I don't know if it's a very clean one.

I added layoutIfNeeded() in configureCell method :

func configureCell(cell: ParagraphTableViewCell, atIndexPath indexPath: IndexPath) {

    let paragraph = paragraphArray[indexPath.row] as! Paragraph

    cell.layoutIfNeeded() 

    let pauseCharactersArray:[Int:Double] = [1:0, 6:0]
    cell.dialogueLabel.setTextWithTypeAnimation(id:"intro", typedText: "lorem ipsum", pauseCharacterArray: pauseCharactersArray)

}

And I added the heightForRowAtIndexPath method in my controller, which returns the height of my label:

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

    let cell = tableView.dequeueReusableCell(withIdentifier: "ParagraphTableViewCell") as! ParagraphTableViewCell

    return cell.dialogueLabel.frame.height
}

It works, but I don't understand exactly why, because in heightForRowAtIndexPath I'm returning the height of the label and not the height of the cell.

If someone has a better solution, I'm interested.




回答3:


The way you are doing it requires a little bit of refactoring: you need a reference to the tableView inside your TypeWriter function. Anyway, a quick and dirty fix is as follows:

First you add a weak reference to tableView in your cell:

class ParagraphTableViewCell: UITableViewCell {
weak var tableView: UITableView?
[...]
}

Then you go seek for it inside your main cycle, to tell the tableView you are updating stuff:

DispatchQueue.main.async {
                    if let delegate = (self.superview?.superview as? ParagraphTableViewCell)?.tableView {
                        delegate.beginUpdates()
                        self.text = self.text! + String(character)
                        delegate.endUpdates()
                    }
                }

I wouldn't recommend having such approach in an app and you surely want to refactor this into a more solid pattern, my example shows how it works with your code.

I've created a working example to test my code, git it here



来源:https://stackoverflow.com/questions/43118470/how-to-set-the-height-of-a-cell-depending-on-a-uilabel-with-a-typewriter-effect

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!