如何根据具有打字机效果的 UILabel 设置单元格的高度

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

我在 UITableView 的单元格中有一个 UILabel。

根据标签的高度调整单元格的高度,没关系,效果很好。

但我需要添加另一个约束。 我需要显示带有打字机效果的 UILabel(逐个字母)。

我的效果扩展效果很好:

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

我试图在我的 configureCell 基金中调用这个函数:

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

但是标签没有出现。我认为是因为单元格中标签的高度设置为0,并且从未更新过。

不知道如何调整单元格的高度"in live"(每次显示一个字符)

编辑

我使用自动布局

编辑 2

打字机效果,运行 在没有 UITableView 的简单 UILabel 中:

xib中的单元格集合:

当我运行时,UILabel没有出现:

我找到了一个解决方案,但我不知道它是否是一个非常干净的解决方案。

我在 configureCell 方法中添加了 layoutIfNeeded() :

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)

}

并且我在我的控制器中添加了 heightForRowAtIndexPath 方法,returns 我的标签的高度:

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

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

    return cell.dialogueLabel.frame.height
}

它有效,但我不明白为什么,因为在 heightForRowAtIndexPath 中我返回标签的高度而不是单元格的高度。

如果有人有更好的解决方案,我很感兴趣。

我能够使用不同的方法实现。

  • 我创建了一个堆栈视图来对标签和按钮进行分组(以及一个子堆栈视图来排列您的按钮)。
  • 在按钮的堆栈视图上,我通过 IB 固定了高度。
  • 在父堆栈视图中。我将堆栈视图固定到单元格的边距。
  • 在标签上,我固定了两侧以适合堆栈视图(不要固定底部,您可以自由固定顶部)

这是我的 IB Screen Shot 以供参考。

  • 在您的 ViewController 上,设置以下属性:

    yourTableView.rowHeight = UITableViewAutomaticDimension
    yourTableView.rowHeight.estimatedRowHeight = 40 // You should set an initial estimated row height here, the number 40 was chosen arbitrarily
    
  • 我编辑了你的方法以添加回调。

    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
      }
    }
    
  • 并且通过回调,我在每次字符更新后从 TableView 调用了 beginUpdates()endUpdates()

希望这对您有所帮助。

您执行此操作的方式需要进行一些重构:您需要在 TypeWriter 函数中引用 tableView。无论如何,一个快速而肮脏的修复如下:

首先在单元格中添加对 tableView 的弱引用:

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

然后你在你的主循环中寻找它,告诉 tableView 你正在更新东西:

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

我不建议在应用程序中采用这种方法,并且您肯定希望将其重构为更可靠的模式,我的示例展示了它如何与您的代码一起使用。

我创建了一个工作示例来测试我的代码,git它here