限制UITextview的行数

83

我想知道如何在编辑UITextField时限制用户输入的行数(而不是像其他问题中询问的字符数)。

理想情况下,我要将输入限制为最多10行。

我需要从哪里开始? 我是否需要使用方法来完成这个功能?

 - (BOOL)textViewShouldBeginEditing:(UITextView *)aTextView

你可以在这篇文章中找到答案:https://dev59.com/i0bRa4cB1Zd3GeqPxiNO。 - Zakaria
可能是Limiting text in a UITextView的重复问题。 - santhu
11个回答

262

Maciek Czarnik的回答对我没用,但它让我知道该怎么做。

iOS 7以上

Swift

textView.textContainer.maximumNumberOfLines = 10
textView.textContainer.lineBreakMode = .byTruncatingTail

Objective-C

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

3
现在应该接受这个答案,即使晚了几年,它仍然有效! - Siegfoult
42
这个有漏洞。它允许你在文本框的末尾按回车键,然后你的光标会消失,必须按删除键才能找到它。 - Eric Conner
Swift 2:textView.textContainer.lineBreakMode = .ByTruncatingTail 注:该翻译为技术性语言,仅供参考。 - App Dev Guy

59

也许这可以帮助(适用于iOS 7+):

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

我想即使是第一行也应该行得通,但实际上并没有...也许这是SDK的一个错误


15
您的想法是正确的,但方法有误。每当文本将要更改时,都会调用textView:shouldChangeTextInRange:replacementText: 方法;您可以使用文本视图的text 属性访问文本视图的当前内容,并使用[textView.text stringByReplacingCharactersInRange:range withString:replacementText]根据传递的范围和替换文本构建新内容。然后您可以计算行数,返回YES以允许更改或NO以拒绝更改。

你是指屏幕上的行,而不是文本的逻辑行吗? - Anomie
实际上,是的,我想知道屏幕上的行数。我想将一个txt文件加载到UITextView中,并将显示在屏幕上的行数限制为10或15行,然后将其余部分放在下一个屏幕上。这就像一个有单独页面的小型文字处理器。这就是为什么我想限制在UITextView中显示的行数(在屏幕上)。但也许有更简单的方法?也许使用UIWebView? - n.evermind
1
如果你只需要分页行为,注意UITextViewUIScrollView的子类,因此具有pagingEnabled属性。 - Anomie
我认为这样不起作用,因为 shouldChangeTextInRange 只能计算新输入之前的行数。每次新文本是“\n”时添加一行也不起作用,因为用户可能自然地达到行尾。 - Declan McKenna

14

Swift 3.0 版本中:

self.textView.textContainer.maximumNumberOfLines = self.textViewNumberOflines
self.textView.textContainer.lineBreakMode = .byTruncatingTail

12

Maciek Czarnik的回答对我似乎不起作用,即使在iOS7上也是如此。它给了我奇怪的行为,我不知道为什么。

我所做的就是限制UITextView中行数的数量:

(仅在iOS7中测试)在以下UITextViewDelegate方法中:

- (void)textViewDidChange:(UITextView *)textView
{
    NSUInteger maxNumberOfLines = 5;
    NSUInteger numLines = textView.contentSize.height/textView.font.lineHeight;
    if (numLines > maxNumberOfLines)
    {
        textView.text = [textView.text substringToIndex:textView.text.length - 1];
    }
}

CGSize size = [textView sizeThatFits:CGSizeMake(textView.width, MAXFLOAT)]; 对我来说,我必须计算出新的高度才能使其正常工作。 - tettoffensive
很好的解决方案,简单而完美。 - AFTAB MUHAMMED KHAN

7
这是一个改进后的Numereyes答案版本,使用的是Swift 4.2 / Swift 5。
我创建了一个小扩展程序,以便可以重复使用代码。我使用While-Loop来检查大小是否合适。当用户一次性粘贴大量文本时,这也可以工作。
extension UITextView {        
    var numberOfCurrentlyDisplayedLines: Int {
        let size = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        //for Swift <=4.0, replace with next line:
        //let size = systemLayoutSizeFitting(UILayoutFittingCompressedSize)

        return Int(((size.height - layoutMargins.top - layoutMargins.bottom) / font!.lineHeight))
    }

    /// Removes last characters until the given max. number of lines is reached
    func removeTextUntilSatisfying(maxNumberOfLines: Int) {
        while numberOfCurrentlyDisplayedLines > (maxNumberOfLines) {
            text = String(text.dropLast())
            layoutIfNeeded()
        }
    }
}

// Use it in UITextView's delegate method:
func textViewDidChange(_ textView: UITextView) {        
    textView.removeTextUntilSatisfying(maxNumberOfLines: 10)
}        

这似乎依赖于文本中存在的换行符,如果没有任何换行符,则不起作用,即使有多个可见/显示的行,它始终报告0行。 - kwiknik
@kwiknik 我认为你想要评论这个答案:https://dev59.com/xG435IYBdhLWcg3wxDBp#54924572? - heyfrank
我能理解你为什么感到困惑,让我更好地解释一下:当我尝试了你的解决方案并且文本包含换行符时,它可以工作。但是当文本没有换行符但仍然可见地分成多行时,你提供的代码没有计算出可见行数,而是返回了零。 - kwiknik

5

Swift 4

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    let existingLines = textView.text.components(separatedBy: CharacterSet.newlines)
    let newLines = text.components(separatedBy: CharacterSet.newlines)
    let linesAfterChange = existingLines.count + newLines.count - 1
    return linesAfterChange <= textView.textContainer.maximumNumberOfLines
}

如果你想限制字符数量:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        let existingLines = textView.text.components(separatedBy: CharacterSet.newlines)
        let newLines = text.components(separatedBy: CharacterSet.newlines)
        let linesAfterChange = existingLines.count + newLines.count - 1
        if(text == "\n") {
            return linesAfterChange <= textView.textContainer.maximumNumberOfLines
        }

        let newText = (textView.text as NSString).replacingCharacters(in: range, with: text)
        let numberOfChars = newText.count
        return numberOfChars <= 30 // 30 characters limit
    }
}

不要忘记在viewDidLoad中添加您想要限制的行数:

txtView.textContainer.maximumNumberOfLines = 2

3
其他提供的解决方案不能解决一个相关问题,即在末尾创建了一行(在问题的情况下是第11行)。
以下是一个工作的解决方案,使用Swift 4.0和Xcode 9.0 beta(在这篇博客文章中找到)。
   class ViewController: UIViewController, UITextViewDelegate {

      @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()
        textView.delegate = self
        textView.textContainer.maximumNumberOfLines = 10
        textView.textContainer.lineBreakMode = .byWordWrapping
      }

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

        let existingLines = textView.text.components(separatedBy: CharacterSet.newlines)
        let newLines = text.components(separatedBy: CharacterSet.newlines)
        let linesAfterChange = existingLines.count + newLines.count - 1

        return linesAfterChange <= textView.textContainer.maximumNumberOfLines
    }
< p > < em > 注意: < /em > 这个解决方案不能处理最后一行太长无法显示的情况(文本将被隐藏在 UITextView 的右侧)。< /p >

1

与其他答案类似,但可以直接从Storyboard使用,无需子类化:

extension UITextView {
    @IBInspectable var maxNumberOfLines: NSInteger {
        set {
            textContainer.maximumNumberOfLines = maxNumberOfLines
        }
        get {
            return textContainer.maximumNumberOfLines
        }
    }
    @IBInspectable var lineBreakByTruncatingTail: Bool {
        set {
            if lineBreakByTruncatingTail {
                textContainer.lineBreakMode = .byTruncatingTail
            }
        }
        get {
            return textContainer.lineBreakMode == .byTruncatingTail
        }
    }
}

0

请参考苹果文档:计算文本行数 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html

    - (void)textViewDidChangeSelection:(UITextView *)textView{
NSLayoutManager *layoutManager = [textView layoutManager];
unsigned numberOfLines, index, numberOfGlyphs =
(unsigned)[layoutManager numberOfGlyphs];
NSRange lineRange;
for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){
    (void) [layoutManager lineFragmentRectForGlyphAtIndex:index
            effectiveRange:&lineRange];
    index = (unsigned)NSMaxRange(lineRange);
}
if(numberOfLines > 10){
    self.textField.text = self.lastString;
}

}

处理 "\n"

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
if([text containsString:@"\n"]){
    NSLayoutManager *layoutManager = [textView layoutManager];
    unsigned numberOfLines, index, numberOfGlyphs =
            (unsigned)[layoutManager numberOfGlyphs];
    NSRange lineRange;
    for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){
        (void) [layoutManager lineFragmentRectForGlyphAtIndex:index
                effectiveRange:&lineRange];
        index = (unsigned)NSMaxRange(lineRange);
    }
    NSLog(@"lines = %d",numberOfLines);
    if(numberOfLines >= 10){
        return NO;
    }
}
self.lastString = textView.text;

return YES;

}


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