How to set cell height depending on UILabel with typewriter effect

I have a UILabel in a UITableView cell.

To adjust the height of the cell depending on the height of the mark, this is normal, it works great.

But I need to add one more restriction. I need to display a 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 calling this function in my configureCell foundation:

 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 does not appear. I think this is because the height of the label in the cell is 0, and it never gets updated.

I don’t know how to adjust the height of the cell live (every time the symbol is displayed)

EDIT

I am using autolayout

EDIT 2

The typewriter effect works in a simple UILabel without a UITableView:

gif

Cell installed in xib:

xib

When I start, UILabel does not appear:

run

+5
source share
3 answers

I managed to achieve another method.

  • I created a stack view to group a shortcut and buttons (and an additional stack view to align your buttons).
  • In the button stack view, I anchored the height through IB.
  • In the view of the parent stack. I tied the stack view to the cell marker.
  • On the label, I attached both sides to the stack view (do not attach the bottom side, you can attach the top side)

Here is my IB Screen Shot for use as a 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 a 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 beginUpdates() and endUpdates() from the TableView after each character update.

Hope this helps you anyway.

+4
source

I found a solution, but I don’t know if it is very clean.

I added layoutIfNeeded () to the 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 method heightForRowAtIndexPath to 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 } 

This works, but I don’t understand why, because in heightForRowAtIndexPath I return the height of the label, not the height of the cell.

If anyone has a better solution, I wonder.

+2
source

How you do this requires a 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 the tableView in your cell:

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

Then you look for it in your main loop to tell tableView that you are updating the material:

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

I would not recommend using this approach in an application, and you probably want to reorganize it into a more robust template, my example shows how it works with your code.

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

+2
source

Source: https://habr.com/ru/post/1266125/


All Articles