【问题标题】:How to limit the number of lines in UITextView?如何限制 UITextView 中的行数?
【发布时间】:2023-03-27 22:02:02
【问题描述】:

我的屏幕上有一个 UITextView,用户应该只填写 两行,当用户在第二行时,返回键应该变成 Done

如何限制 UITextView 中的数量? 我搜索了很多,没有任何有用的结果!

我为 Swift 找到了这个答案:

locationNoteTextView.textContainer.maximumNumberOfLines = 2
self.locationNoteTextView.textContainer.lineBreakMode = NSLineBreakMode.ByClipping

不行,这样用户可以输入无穷大的字符,但是在屏幕上看到的只有两行!

所以如果你尝试打印 textView 的文本,你会发现一个灾难文本。

【问题讨论】:

  • 仅供参考我试过了!!!!!

标签: ios swift uitextview


【解决方案1】:

您需要实现textView:shouldChangeTextInRange:replacementText:。每当文本将要更改时,都会调用此方法。您可以使用其 text 属性访问文本视图的当前内容。

使用[textView.text stringByReplacingCharactersInRange:range withString:replacementText] 从传递的范围和替换文本构造新内容。

然后您可以计算行数并返回YES 以允许更改或NO 拒绝更改。

编辑:应 OP 请求:

斯威夫特:

func sizeOfString (string: String, constrainedToWidth width: Double, font: UIFont) -> CGSize {
    return (string as NSString).boundingRectWithSize(CGSize(width: width, height: DBL_MAX),
        options: NSStringDrawingOptions.UsesLineFragmentOrigin,
        attributes: [NSFontAttributeName: font],
        context: nil).size
}


func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    let newText = (textView.text as NSString).stringByReplacingCharactersInRange(range, withString: text)
    var textWidth = CGRectGetWidth(UIEdgeInsetsInsetRect(textView.frame, textView.textContainerInset))
    textWidth -= 2.0 * textView.textContainer.lineFragmentPadding;

    let boundingRect = sizeOfString(newText, constrainedToWidth: Double(textWidth), font: textView.font!)
    let numberOfLines = boundingRect.height / textView.font!.lineHeight;

    return numberOfLines <= 2;
}

Obj-C

- (CGSize) sizeOfString:(NSString*)str constrainedToWidth:(CGFloat)width andFont:(UIFont*)font
{
    return [str boundingRectWithSize:CGSizeMake(width, DBL_MAX)
                              options:NSStringDrawingUsesLineFragmentOrigin
                           attributes:@{NSFontAttributeName: font}
                              context:nil].size;
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
    NSString *newText = [textView.text stringByReplacingCharactersInRange:range withString:text];
    CGFloat textWidth = CGRectGetWidth(UIEdgeInsetsInsetRect(textView.frame, textView.textContainerInset));
    textWidth -= 2.0 * textView.textContainer.lineFragmentPadding;
    CGSize boundingRect = [self sizeOfString:newText constrainedToWidth:textWidth andFont:textView.font];
    int numberOfLines = boundingRect.height / textView.font.lineHeight;
    return numberOfLines <= 2;
}

【讨论】:

  • 你的答案中的行数限制在哪里?!
  • 谢谢,但您能解释一下吗?我不明白 [textView.text stringByReplacingCharactersInRange:range withString:replacementText] 如何帮助我?
  • @Abhinav 该问题被标记为“swift”,因此最好在 Swift 中提供您的答案。谢谢。
  • 添加了 swift 版本。免责声明:我只是快速将其转换为 Swift,没有机会测试,所以请多多包涵。感谢您的指点:)@EricD。
  • #1 如果您添加一个空格作为最后一个字符,您可以继续添加空格。 #2 如果你达到极限然后按两次空格键,textview 甚至会增加一行。有人有什么建议可以避免这种行为吗?
【解决方案2】:

适用于 iOS 7+

textView.textContainer.maximumNumberOfLines = 10;
textView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail;

textView.textContainer.maximumNumberOfLines = 10;
[textView.layoutManager textContainerChangedGeometry:textView.textContainer];

【讨论】:

    【解决方案3】:

    如果有人想要实时行号计数,这就是使用 RxSwift 和 RxCocoa 的方法

        let disposeBag = DisposeBag()
    
        textView.rx_text.asDriver().driveNext { text in
    
            let text = text as NSString
    
            var textWidth: CGFloat = CGRectGetWidth(UIEdgeInsetsInsetRect(self.textView.frame, self.textView.textContainerInset))
    
            textWidth -= 2.0 * self.textView.textContainer.lineFragmentPadding
    
            let textAttributes = [NSFontAttributeName: UIFont(name: “Set your font here”, size: 26.0)!]
    
            let boundingRect: CGRect = text.boundingRectWithSize(CGSizeMake(textWidth, 0), options: [NSStringDrawingOptions.UsesLineFragmentOrigin, NSStringDrawingOptions.UsesFontLeading], attributes: textAttributes, context: nil)
    
            let font = UIFont(name: “Set your font here”, size: 26.0)
    
            guard let lineHeight = font?.lineHeight else {return}
    
            let numberOfLines = CGRectGetHeight(boundingRect) / lineHeight
    
            if numberOfLines <= 2 {
    
    
                self.textView.typingAttributes = [NSFontAttributeName: UIFont(name: “Set your font here”, size: 26.0)!, NSForegroundColorAttributeName: “Set your color here”]
    
            } else {
    
                self.textView.typingAttributes = [NSFontAttributeName: UIFont(name: “Set your font here”, size: 18.0)!, NSForegroundColorAttributeName: “Set your color here”]
    
            }
    
        }.addDisposableTo(disposeBag)
    

    【讨论】:

      【解决方案4】:

      我将@Abhinav 代码更新为 Swift 5,还添加了对限制字符数的支持。

      import UIKit
      
      class ViewController: UIViewController {
          @IBOutlet private var textView: UITextView!
      
          private let maxNumberOfLines = 4
          private let maxCharactersCount = 100
      
          override func viewDidLoad() {
              super.viewDidLoad()
              textView.delegate = self
          }
      }
      
      extension ViewController: UITextViewDelegate {
          private func textView(_ textView: UITextView, newText: String, underLineLimit: Int) -> Bool {
              guard let textViewFont = textView.font else {
                  return false
              }
      
              let rect = textView.frame.inset(by: textView.textContainerInset)
              let textWidth = rect.width - 2.0 * textView.textContainer.lineFragmentPadding
      
              let boundingRect = newText.size(constrainedToWidth: textWidth,
                                              font: textViewFont)
      
              let numberOfLines = boundingRect.height / textViewFont.lineHeight
      
              let underLimit = Int(numberOfLines) <= underLineLimit
              return underLimit
          }
      
          func textView(_ textView: UITextView,
                        shouldChangeTextIn range: NSRange,
                        replacementText text: String) -> Bool {
      
              let newText = textView.text.replacingCharacters(in: range, with: text)
      
              let underLineLimit = self.textView(textView, newText: newText, underLineLimit: maxNumberOfLines)
              let underCharacterLimit = newText.count <= maxCharactersCount
              return underLineLimit && underCharacterLimit
          }
      }
      
      extension String {
          func replacingCharacters(in range: NSRange, with string: String) -> String {
              let currentNSString = self as NSString
              return currentNSString.replacingCharacters(in: range, with: string)
          }
      
          func size(constrainedToWidth width: CGFloat, font: UIFont) -> CGSize {
              let nsString = self as NSString
              let boundingSize = CGSize(width: width, height: .greatestFiniteMagnitude)
              return nsString
                  .boundingRect(with: boundingSize,
                                options: .usesLineFragmentOrigin,
                                attributes: [.font: font],
                                context: nil)
                  .size
          }
      }
      

      【讨论】:

        【解决方案5】:

        这是@Abhinav 的答案,但在 Swift

        func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
        
        
            let newText = (textView.text as NSString).stringByReplacingCharactersInRange(range, withString: text)
        
            let textAttributes = [NSFontAttributeName: textView.font]
        
            var textWidth: CGFloat = CGRectGetWidth(UIEdgeInsetsInsetRect(textView.frame, textView.textContainerInset))
        
            textWidth -= 2.0 * textView.textContainer.lineFragmentPadding
        
            let boundingRect: CGRect = newText.boundingRectWithSize(CGSizeMake(textWidth, 0), options: NSStringDrawingOptions.UsesLineFragmentOrigin|NSStringDrawingOptions.UsesFontLeading, attributes: textAttributes, context: nil)
        
          var numberOfLines = CGRectGetHeight(boundingRect) / textView.font.lineHeight;
        
            return numberOfLines <= 2;
        }
        

        【讨论】:

          【解决方案6】:

          这是@Abhinav 为 Swift 3

          更新的答案
          func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
          
              guard maxLines > 0 else {
                  return true
              }
          
              let newText = (textView.text as NSString).replacingCharacters(in: range, with: text)
          
              let textAttributes = [NSAttributedStringKey.font : textView.font!]
          
              var textWidth: CGFloat = UIEdgeInsetsInsetRect(textView.frame, textView.textContainerInset).width
          
              textWidth -= 2.0 * textView.textContainer.lineFragmentPadding
          
              let boundingRect: CGRect = newText.boundingRect(with: CGSize(width: textWidth, height: 0), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: textAttributes, context: nil)
          
              let numberOfLines = Int(floor(boundingRect.height / textView.font!.lineHeight))
          
              return numberOfLines <= maxLines
          
          }
          

          ma​​xLines 是您希望将 textView 限制为的行数。如果为 0,则行数不受限制。

          【讨论】:

            【解决方案7】:

            在下面找到 Abhinav 答案的 Objective C 版本:

            -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
            {
                NSString *newText = [textView.text stringByReplacingCharactersInRange:range withString:text];
                CGFloat textWidth = CGRectGetWidth(UIEdgeInsetsInsetRect(textView.frame, textView.textContainerInset));
                textWidth -= 2.0 * textView.textContainer.lineFragmentPadding;
            
                CGSize boundingRect = [self sizeOfString:newText constrainedToWidth:textWidth font:textView.font];
                NSInteger numberOfLines = boundingRect.height / textView.font.lineHeight;
            
                return numberOfLines <= 2;
            }
            
            -(CGSize)sizeOfString:(NSString *)string constrainedToWidth:(double)width font:(UIFont *)font
            {
                return  [string boundingRectWithSize:CGSizeMake(width, DBL_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: font} context:nil].size;
            }
            

            【讨论】:

              猜你喜欢
              • 2011-12-12
              • 2011-07-10
              • 1970-01-01
              • 2011-11-12
              • 2012-12-13
              • 2015-10-19
              • 2011-01-30
              • 2015-10-20
              相关资源
              最近更新 更多