当UITextView文本超过一行时,扩展UITextView和UITableView。

9

我有一个UITableView,每个单元格内都有两个并排的UITextView。我希望UITableViewCell和UITextView都能增加高度,这样用户就不需要在UITextView内滚动。以下是我的尝试:

在TableViewController类中:

    self.tableView.estimatedRowHeight = 44
    self.tableView.rowHeight = UITableViewAutomaticDimension

在 TableViewCell 类中(从这里获取):
func textViewDidChange(textView: UITextView) {

    var frame : CGRect = textView.frame
    frame.size.height = textView.contentSize.height
    textView.frame = frame
}

当用户在UITextView中输入的内容超过设置的宽度时,UITableView的高度会从44增加到约100,而UITextView的高度不会增加。我已经设置了约束,使UITextView的高度等于UITableViewCell的高度。
有什么想法为什么会发生这种情况,以及如何正确动态更改UITextView和UITableView的高度?
2个回答

17
我的回答是基于我们在社交应用Impether的生产中实际使用的内容,因为你在Twitter上问我是否使用过该应用程序,并且在那里看到了扩展的UITextView。
首先,我们有一个自定义的UITableViewCell类,其中包含要扩展的UITextView(该类还有相应的xib文件,您可以自行设计):
class MultiLineTextInputTableViewCell: UITableViewCell {

    //our cell has also a title, but you
    //can get rid of it
    @IBOutlet weak var titleLabel: UILabel!
    //UITextView we want to expand
    @IBOutlet weak var textView: UITextView!

    override init(style: UITableViewCellStyle, reuseIdentifier: String!) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    /// Custom setter so we can initialize the height of the text view
    var textString: String {
        get {
            return textView?.text ?? ""
        }
        set {
            if let textView = textView {
                textView.text = newValue
                textView.delegate?.textViewDidChange?(textView)
            }
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()        
        // Disable scrolling inside the text view so we enlarge to fitted size
        textView?.scrollEnabled = false        
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        if selected {
            textView?.becomeFirstResponder()
        } else {
            textView?.resignFirstResponder()
        }
    }
}

有了定义的自定义单元格,您可以在基于 UITableViewController 的类中使用它,如下所示:
class YourTableViewController: UITableViewController {

    //in case where you want to have
    //multiple expanding text views
    var activeTextView: UITextView?

    override func viewDidLoad() {
        super.viewDidLoad()

        //registering nib for a cell to reuse
        tableView.registerNib(
            UINib(nibName: "MultiLineTextInputTableViewCell", bundle: nil),
            forCellReuseIdentifier: "MultiLineTextInputTableViewCell")

    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        //hide keyboard when view controller disappeared
        if let textView = activeTextView {
            textView.resignFirstResponder()
        }
    }

    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        //put your value here
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //put your value here
        return 2
    }

    override func tableView(tableView: UITableView,
                            cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let row = indexPath.row
        let cell = tableView.dequeueReusableCellWithIdentifier(
            "MultiLineTextInputTableViewCell",
            forIndexPath: indexPath) as! MultiLineTextInputTableViewCell

        let titleText = "Title label for your cell"
        let textValue = "Text value you want for your text view"

        cell.titleLabel.text = titleText
        cell.textView.text = textValue

        //store row of a cell as a tag, so you can know
        //which row to reload when the text view is expanded
        cell.textView.tag = row
        cell.textView.delegate = self

        return cell

    }

    override func tableView(tableView: UITableView,
                            estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        //standard row height
        return 44.0
    }

    override func tableView(tableView: UITableView,
                            heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return UITableViewAutomaticDimension
    }

    // Override to support conditional editing of the table view.
    override func tableView(tableView: UITableView,
                            canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // Return false if you do not want the specified item to be editable.
        return true
    }
}

//extension containing method responsible for expanding text view
extension YourTableViewController: UITextViewDelegate {

    func textViewDidEndEditing(textView: UITextView) {
        let value = textView.text
        //you can do something here when editing is ended
    }

    func textView(textView: UITextView, shouldChangeTextInRange range: NSRange,
                  replacementText text: String) -> Bool {
        //if you hit "Enter" you resign first responder
        //and don't put this character into text view text
        if text == "\n" {
            textView.resignFirstResponder()
            return false
        }
        return true
    }

    func textViewDidBeginEditing(textView: UITextView) {
        activeTextView = textView
    }

    //this actually resize a text view
    func textViewDidChange(textView: UITextView) {

        let size = textView.bounds.size
        let newSize = textView.sizeThatFits(CGSize(width: size.width,
            height: CGFloat.max))

        // Resize the cell only when cell's size is changed
        if size.height != newSize.height {
            UIView.setAnimationsEnabled(false)
            tableView?.beginUpdates()
            tableView?.endUpdates()
            UIView.setAnimationsEnabled(true)

            let thisIndexPath = NSIndexPath(forRow: textView.tag, inSection: 0)
            tableView?.scrollToRowAtIndexPath(thisIndexPath,
                                              atScrollPosition: .Bottom,
                                              animated: false)
        }
    }
} 

谢谢你的回答!我似乎无法让它工作。我有两个并排的TextView。两者都没有设置高度约束,但其中一个有宽度约束。我尝试打开滚动启用,并且单元格仍然不会扩展,TextView只是被推上去以适应新行。有什么想法吗? - Dan Levy
救了我的一天! - Codetard

1

首先确保您的自动布局约束不与您设置的框架冲突。(如果一切正常但仍无法工作)然后尝试将frame更改为bounds。视图的框架(CGRect)是其矩形在父视图坐标系中的位置。根据我的经验,有时使用frame可能会导致奇怪的问题。

func textViewDidChange(textView: UITextView) {

    var frame : CGRect = textView.bounds
    frame.size.height = textView.contentSize.height
    textView.bounds = frame
}

我仍然遇到同样的问题。我的TextView并排放置,没有设置高度约束。我应该在自定义单元格类中调用textViewDidChange吗?还是可以在我的tableVC中自动设置行高? - Dan Levy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接