【问题标题】:UITableViewCell with multiline input field?具有多行输入字段的 UITableViewCell?
【发布时间】:2016-09-13 08:56:48
【问题描述】:

我想创建一个带有多行输入字段(文本视图)的 uitableview 单元格。当用户输入文本时,文本视图或单元格的高度应自动增加。

【问题讨论】:

  • 我明白你在问什么:你想要一个 UITableView ,用户可以在其中编辑行中的文本。然后,您希望文本字段根据需要实时扩展以容纳文本?
  • 是的,这正是我想要的。

标签: ios swift uitableview autolayout


【解决方案1】:

您好:这一直困扰着我一整天,但我只是把一些我认为已经破解的东西放在一起。我假设您知道如何将基本的 tableView 放在一起。

您需要为每一行添加一个 UITextView,并且至关重要的是,让您的 tableViewController 成为每个 UITextView 的委托。除了每一行的字符串值数组外,您还需要每行都有一个高度数组(CGFloats)。另一个关键方面是为每个 textView 分配一个与单元格的 indexPath.row 相等的数字标签,这使我们能够跟踪我们正在处理的 textView。这将作为一个相同值的数组开始,但想法是当文本变得对于现有框架而言太大时,您可以根据需要更新此数组,如下所示。

然后我实现了以下两个委托函数以及一个定制函数,以根据需要更新布局:

func updateLayout(forTextView: UITextView) -> Void {
    guard forTextView.tag < self.data.count else { return } // Make sure we're not going to try and access a value that does not exist

    self.data[forTextView.tag] = forTextView.text // Update our data array to reflect the new string value for the cell's text view

    let newHeight : CGFloat = forTextView.frame.height // The new cell height is based on the height of the text view
    let oldHeight : CGFloat = self.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: forTextView.tag, inSection: 0))!.frame.height // Retrieving the existing height of the cell

    self.heights[forTextView.tag] = newHeight + 2 // Set the new value in the heights array with a 1 point buffer
    // Now animate to ease the transition
    UIView.animateWithDuration(0.25) { 
        for cell in self.tableView.visibleCells {
            if let indexPath = self.tableView.indexPathForCell(cell) {
                // We only need to update the edited cell and all cells below it
                if indexPath.row > forTextView.tag {
                    cell.frame = CGRect(origin: CGPointMake(cell.frame.origin.x, cell.frame.origin.y + forTextView.frame.height + 2 - oldHeight), size: CGSizeMake(cell.frame.width, self.heights[indexPath.row]))
                }
                else if indexPath.row == forTextView.tag
                {
                    cell.frame = CGRect(origin: cell.frame.origin, size: CGSizeMake(cell.frame.width, self.heights[indexPath.row]))
                }
            }
        }
    }
}

func textViewDidEndEditing(textView: UITextView) {
    // Update the cell and exit
    self.updateLayout(textView)
}

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    // More complicated: we need to update the frame but also maintain focus in this textView as the user has not finished editing yet
    var textFrame : CGRect = textView.frame
    let newHeight : CGFloat = ceil(textView.contentSize.height)
    if newHeight - textFrame.height > 0 {
        textFrame.size.height = ceil(textView.contentSize.height)
        textView.frame = textFrame
        textView.setContentOffset(CGPointMake(0, 5), animated: false)
        self.updateLayout(textView)
    }

    // This code puts an active cursor back at the end of the textView's text
    textView.selectedRange = NSRange(location: textView.text.characters.count, length: 0)
    textView.becomeFirstResponder()
    return true
}

棘手的部分是更新 tableView 的布局,同时将光标的焦点保持在正在积极编辑的字段中。

我对在文本字段需要扩展时提供平滑过渡的结果感到非常满意。

我不想把完整的代码发给你,因为最好自己把这些东西放在一起;但是,如果您真的被卡住了,请给我留言,我会发送给您。万事如意!

【讨论】:

    【解决方案2】:

    我找到了比上述答案更简单的方法。使用 UITextView 创建一个自定义单元格,如下所示:

    private lazy var valueTextField: UITextView = {
        let textField = UITextView()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.layer.borderWidth = 0.8
        textField.layer.borderColor = UIColor(displayP3Red: 204/255.0, green: 204/255.0, blue: 204255.0, alpha: 1).cgColor
        textField.layer.cornerRadius = 5
        textField.textColor = .black
        textField.isUserInteractionEnabled = true
        textField.isEditable = true
        textField.isScrollEnabled = false
        textField.textContainer.lineBreakMode = .byWordWrapping
        textField.delegate = self
        return textField
    }()
    

    根据需要更改属性。

    现在在视图中添加这个 UiTextView。比如:

    view.addSubView(valueTextField)
    

    然后创建类似如下的回调:

    var textChanged: ((String) -> Void)?
    
    func textViewDidChange(_ textView: UITextView) {
        textChanged?(textView.text)
    }
    
    func textChanged(action: @escaping (String) -> Void) {
        textChanged = action
    }
    

    然后在 TableViewController 中的 cellForRowAt 内部实现回调并使用 begin 和 end 更新调用刷新 tableview:

    (cell as! CustomCell).textChanged {[weak tableView] (newText: String) in
        DispatchQueue.main.async {
            tableView?.beginUpdates()
            tableView?.endUpdates()
        }
    }
    

    就是这样。您的 textView 应该按预期工作。

    【讨论】:

      猜你喜欢
      • 2011-10-29
      • 2018-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-07
      • 1970-01-01
      • 2018-08-14
      相关资源
      最近更新 更多