UITableViewCell具有多行输入字段?

时间:2016-09-13 08:56:48

标签: ios swift uitableview autolayout

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

2 个答案:

答案 0 :(得分:0)

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

您需要为每一行添加UITextView,并且至关重要的是,使tableViewController成为每个UITextView的委托。除了每行的String值数组外,还需要为每一行设置一个高度数组(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的布局更新,同时将光标的焦点保持在被主动编辑的字段中。

我对结果很满意,当文本字段需要扩展时,该结果可以平滑过渡。

我宁愿不向您发送完整的代码,因为将这些东西放在一起总是更好;但是,如果你真的卡住了我,我会把它发送给我。一切都好!

答案 1 :(得分:0)

我找到了比上述答案更简单的方法。使用 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 中实现回调并使用开始和结束更新调用刷新 tableview:

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

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