当 UITextView 的文本超出 1 行时展开 UITextView 和 UITableView

Posted

技术标签:

【中文标题】当 UITextView 的文本超出 1 行时展开 UITextView 和 UITableView【英文标题】:Expand UITextView and UITableView When UITextView's Text Extends Beyond 1 Line 【发布时间】:2016-05-03 21:45:38 【问题描述】:

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

在 TableViewController 类中:

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

在 TableViewCell 类中(从 here 得到这个):

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 的高度?

【问题讨论】:

【参考方案1】:

我的回答是基于我们在生产社交应用 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 只是被向上推以适应新行。有什么想法吗? 救了我该死的一天!【参考方案2】:

首先确保您的自动布局约束与您设置的框架不冲突。 (如果一切正常但仍然无法正常工作)然后尝试将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 中的自动行高可以吗?

以上是关于当 UITextView 的文本超出 1 行时展开 UITextView 和 UITableView的主要内容,如果未能解决你的问题,请参考以下文章

当 UITextView 大小发生变化时,如何调整 UITableViewCell 的大小?

当新行添加到 UITextView 实例中时,如何向上滚动视图?

选择 UITableView 行时使用特定信息填充 UITextView

可变高度 UITextView

iOS 开发-UITextView(第二种输入框)的使用

在 UITableViewCell 内展开 UITextView