【问题标题】:How to change the TextView height dynamically to a threshold and then allow scrolling?如何将 TextView 高度动态更改为阈值然后允许滚动?
【发布时间】:2016-11-19 19:53:48
【问题描述】:

我有一个TextView,它的最小高度限制为 33。故事板中禁用了滚动。 TextView 应该根据内容增加高度,直到达到最大高度 100。然后我将 scrollEnabled 更改为 true,将 TextView 的高度更改为最大高度 100,但高度更改为 33。我该如何解决这个问题?

import UIKit

class ViewController: UIViewController, UITextViewDelegate {

    @IBOutlet weak var messageTextView: UITextView!
    let messageTextViewMaxHeight: CGFloat = 100
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.messageTextView.delegate = self
    }
    
    func textViewDidChange(textView: UITextView) {
        
        if textView.frame.size.height >= self.messageTextViewMaxHeight {
            textView.scrollEnabled = true
            textView.frame.size.height = self.messageTextViewMaxHeight
        } else {
            textView.scrollEnabled = false
        }
    }
}

【问题讨论】:

    标签: ios swift scroll uitextview


    【解决方案1】:

    解决问题的一种简单方法是在 textViewDidChange 调用时更改 textView 的框架。 UITextView 实际上是一个 UIScrollView。如果必须使用约束,则必须更改约束的常数。 这是我的代码:

    import UIKit
    
    let width = UIScreen.mainScreen().bounds.width
    
    class ViewController: UIViewController, UITextViewDelegate {
    
        var messageTextView: UITextView!
        let messageTextViewMaxHeight: CGFloat = 100
    
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = UIColor.redColor()
            messageTextView = UITextView(frame: CGRectMake(0, 200 - 33, width, 33))
            messageTextView.backgroundColor = UIColor.whiteColor()
            view.addSubview(messageTextView)
            self.messageTextView.delegate = self
        }
    
        //
        let bottomY: CGFloat = 200
    
        func textViewDidChange(textView: UITextView) {
            let formerRect = textView.frame
    
            if formerRect.height > messageTextViewMaxHeight{
    
            }
            else{
                textView.frame = CGRectMake(formerRect.origin.x, bottomY - textView.contentSize.height, width, textView.contentSize.height)
            }
        }
    } 
    

    【讨论】:

    • 更多,可以在TextView的frame改变时添加动画。
    【解决方案2】:

    也许下面的代码更好一点。

    import UIKit
    
    let width = UIScreen.mainScreen().bounds.width
    
    class ViewController: UIViewController, UITextViewDelegate {
    
        var messageTextView: UITextView!
        let messageTextViewMaxHeight: CGFloat = 100
    
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = UIColor.redColor()
            messageTextView = UITextView(frame: CGRectMake(0, 200 - 33, width, 33))
            messageTextView.backgroundColor = UIColor.whiteColor()
            view.addSubview(messageTextView)
            self.messageTextView.delegate = self
        }
    
        //
        let bottomY: CGFloat = 200
    
        func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
            if text == "\n"{
                let formerRect = textView.frame
                let contentSize = textView.contentSize
                if contentSize.height < formerRect.height{
                    //
                }
                else{
                    if contentSize.height >= messageTextViewMaxHeight{
                        //
                    }
                    else{
                        textView.frame = CGRectMake(formerRect.origin.x, bottomY - contentSize.height, width, contentSize.height)
                    }
                }
            }
            return true
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      在表格视图单元格中的文本视图出现许多小时的问题之后,这是对我有用的解决方案。我正在使用 Masonry,但也可以在 IB 中创建约束。

      请注意,使用了 textview 委托。这是有利的,因为无论您是通过编程方式还是通过用户输入来更改文本都没有关系。无论哪种方式,只要文本视图更改其内容,就会调用 layoutSubviews。

      如果您想直接在视图控制器中使用它,可以使用viewDidLayoutSubviews 而不是layoutSubviews

      @implementation TextViewCell
      
      - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
          self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
          if (self){
              UITextView *textView = [[UITextView alloc] initWithFrame:CGRectZero];
              textView.scrollEnabled = NO;
              self.textView = textView;
              [self.contentView addSubview:self.textView];
      
              [self.textView mas_remakeConstraints:^(MASConstraintMaker *make) {
                  make.leading.equalTo(self.contentView.mas_leadingMargin);
                  make.trailing.equalTo(self.contentView.mas_trailingMargin);
                  make.top.equalTo(self.contentView.mas_topMargin);
                  make.bottom.equalTo(self.contentView.mas_bottomMargin);
                  make.height.lessThanOrEqualTo(@100.0).with.priorityHigh();
                  make.height.greaterThanOrEqualTo(@30.0).with.priorityHigh();
              }];
          }
          return self;
      }
      
      
      - (void)layoutSubviews{
          CGSize size = [self.textView sizeThatFits:CGSizeMake(self.textView.bounds.size.width, 10000)];
          self.textView.scrollEnabled = size.height > 100.0;
          [super layoutSubviews];
      }
      
      @end
      

      【讨论】:

        【解决方案4】:

        您的代码似乎需要进行两次更改,并且可以正常工作。

        1. 提供最大高度为 100,而不是约束的最小高度:

        1. 修改代码如下:

           import UIKit
          
          class ViewController: UIViewController, UITextViewDelegate
          {
              @IBOutlet weak var messageTextView: UITextView!
          
              let messageTextViewMaxHeight: CGFloat = 100
              override func viewDidLoad()
              {
                  super.viewDidLoad()
                  messageTextView.delegate = self
              }
          
              func textViewDidChange(textView: UITextView)
              {
                  if textView.contentSize.height >= self.messageTextViewMaxHeight
                  {
                      textView.scrollEnabled = true
                  }
                  else
                      {
                      textView.frame.size.height = textView.contentSize.height
                      textView.scrollEnabled = false // textView.isScrollEnabled = false for swift 4.0
                  }
              }
          }
          

        【讨论】:

        • 这种方法似乎是最简单的,尽管我相信&lt;= 约束存在一个错误。评论有点长,所以我发布了fix 作为单独的答案
        【解决方案5】:

        这遵循与已接受答案类似的方法,但确保textView 在两种高度状态下都受到完全限制。

        (在接受的答案中有一个错误 - 使用具有&lt;= 关系的高度约束不足以在启用滚动时完全约束textView,因为在这种情况下视图不提供intrinsicContentSize。您可以在 IB 中查看此内容(禁用滚动),或在运行时通过查看调试查看。)

        这就是所有必要的:

        // In IB, set the relation to `=` and the constant to your desired threshold point
        // Notice this is a strong reference (since the constraint may get deactivated) 
        @IBOutlet var textViewHeightConstraint: NSLayoutConstraint!
        
        func textViewDidChange(textView: UITextView)
        {       
            let isOversize = textView.contentSize.height >= textViewHeightConstraint.constant
            textViewHeightConstraint.isActive = isOversize
            textView.isScrollEnabled = isOversize
        }
        

        无需手动设置框架,因为在这两种情况下,自动布局都已涵盖。

        【讨论】:

          【解决方案6】:

          创建一个继承自 UITextView 的类,并将以下内容添加到该类中:

          class CustomTextView: UITextView, UITextViewDelegate {
          
              override init(frame: CGRect, textContainer: NSTextContainer?) {
                  super.init(frame: frame, textContainer: textContainer)
          
                  delegate = self
              }
          
              var maxHeight: CGFloat = 200
          
              override var intrinsicContentSize: CGSize {
                  var size = super.intrinsicContentSize
                  if size.height > maxHeight {
                      size.height = maxHeight
                      isScrollEnabled = true
                  } else {
                      isScrollEnabled = false
                  }
                  return size
              }
          
              override var text: String! {
                  didSet {
                      invalidateIntrinsicContentSize()
                  }
              }
          
              func textViewDidChange(_ textView: UITextView) {
                  invalidateIntrinsicContentSize()
              }
          
          }
          

          注意: - 您可以在创建 CustomTextView 后将 maxHeight 初始化为无穷大并设置 maxHeight。该类可以在应用中任何需要的地方重复使用,并且可以针对不同的场景修改最大高度。

          【讨论】:

            【解决方案7】:

            我的解决方案是,在我的 CostumeView 上,我向 UITextView 添加了一个约束:

             textInput.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
              textInput.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 5),
              textInput.heightAnchor.constraint(lessThanOrEqualToConstant: 100), // very important
              textInput.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor),
              textInput.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor)])
            

            “textInput.heightAnchor.constraint(lessThanOrEqualToConstant: 100)”就是它的诀窍。 所以我将 UITextView 设置为 isScrollEnabled = false:

            textView.isScrollEnabled = false
            

            然后在我的 ViewController 上我添加了 UITextViewDelegate :

            extension InboxMsgVC: UITextViewDelegate {
            

            我终于添加了下面的方法:

            func textViewDidChange(_ textView: UITextView) {
            if textView.contentSize.height >= 100 {
              textView.isScrollEnabled = true
            } else {
              textView.isScrollEnabled = false 
            }
            

            }

            就是这样,它对我来说很好用,Swift 5。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2012-06-26
              • 2014-07-20
              • 1970-01-01
              • 1970-01-01
              • 2015-10-08
              • 1970-01-01
              • 2021-03-11
              相关资源
              最近更新 更多