限制UITextView中的行数

5

问题

有没有一种方法在目标iOS 5.0中“准确地”限制UITextView的行数?

我尝试了什么

我在Stack Overflow上搜索后发现以下链接中曾经问过这些问题:
在UITextView中,如何获取下一个输入将开始另一行的点
限制UITextview的行数
限制UITextView的行数

但是在textView:shouldChangeTextInRange:replacementText:中尝试决定是否返回YES或NO时,我仍然无法获得UITextView中准确的行数。 我尝试使用的代码是限制UITextView文本长度的答案,并且修改后删除了-15的代码如下所示。

- (BOOL)textView:(UITextView *)aTextView shouldChangeTextInRange:(NSRange)aRange replacementText:(NSString*)aText
{
        NSString* newText = [aTextView.text stringByReplacingCharactersInRange:aRange withString:aText];

        // TODO - find out why the size of the string is smaller than the actual width, so that you get extra, wrapped characters unless you take something off
        CGSize tallerSize = CGSizeMake(aTextView.frame.size.width,aTextView.frame.size.height*2); // pretend there's more vertical space to get that extra line to check on
        CGSize newSize = [newText sizeWithFont:aTextView.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];

        if (newSize.height > aTextView.frame.size.height)
            {
            [myAppDelegate beep];
            return NO;
            }
        else
            return YES;
}

我找到了一种方法来获取UITextView中文本的行数。这种方法是通过contentSize属性计算,如下所示:textView.contenSize.height/font.lineHeight。这个方法可以准确地得到UITextView中的行数。但是问题在于,在textView:shouldChangeTextInRange:replacementText:textViewDidChange:方法中获取的contentSize是旧的contentSize。因此,我仍然无法限制UITextView中的行数。

我使用的解决方案

这是一种变通方法,但至少它能够工作。

步骤 1

首先,您需要创建一个临时的新UITextView,其设置与原始UITextView完全相同,但将临时UITextView隐藏在.xib文件中。在这个示例代码中,我将临时UITextView命名为tempTextInputView

步骤 2

在.h文件中添加新的引用输出,如下所示:
@property (retain, nonatomic) IBOutlet UITextView *tempTextInputView;//用于计算带有新文本的UITextView的行数

步骤 3

添加以下代码:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    NSString *newText = [textView.text stringByReplacingCharactersInRange:range withString:text];
    _tempTextInputView.text = newText;

    // Calcualte the number of lines with new text in temporary UITextView
    CGRect endRectWithNewText = [_tempTextInputView caretRectForPosition:_tempTextInputView.endOfDocument];
    CGRect beginRectWithNewText = [_tempTextInputView caretRectForPosition:_tempTextInputView.beginningOfDocument];
    float beginOriginY = beginRectWithNewText.origin.y;
    float endOriginY = endRectWithNewText.origin.y;
    int numberOfLines = (endOriginY - beginOriginY)/textView.font.lineHeight + 1;

    if (numberOfLines > maxLinesInTextView) {// Too many lines
        return NO;
    }else{// Number of lines  will not over the limit
        return YES;
    }
}  

讨论

  1. maxLinesInTextView是一个代表你想要的最大行数的int变量。
  2. 我使用了一个临时的UITextView来设置新文本,因为当我在注音键盘上输入时(这是一种繁体中文输入法),如果直接在原始UITextView中设置新文本,我会遇到一些问题。
  3. 我仍然使用textView:shouldChangeTextInRange:replacementText:而不是textViewDidChange:,是因为当我在全局NSString中缓存修改前的文本并将UITextView.text替换为该全局NSString时,在textViewDidChange:中会出现一些问题。

当光标位于字符串中间时,按回车键换行仍然存在问题。这种解决方案将得到错误的行数。 - Calvin Wu
3个回答

1

以下是如何使用 UITextViewDelegateshouldChangeTextInRange: 方法来限制文本输入的高度:

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    // Combine the new text with the old
    let combinedText = (textView.text as NSString).stringByReplacingCharactersInRange(range, withString: text)

    // Create attributed version of the text
    let attributedText = NSMutableAttributedString(string: combinedText)
    attributedText.addAttribute(NSFontAttributeName, value: textView.font, range: NSMakeRange(0, attributedText.length))

    // Get the padding of the text container
    let padding = textView.textContainer.lineFragmentPadding

    // Create a bounding rect size by subtracting the padding
    // from both sides and allowing for unlimited length 
    let boundingSize = CGSizeMake(textView.frame.size.width - padding * 2, CGFloat.max)

    // Get the bounding rect of the attributed text in the
    // given frame
    let boundingRect = attributedText.boundingRectWithSize(boundingSize, options: NSStringDrawingOptions.UsesLineFragmentOrigin, context: nil)

    // Compare the boundingRect plus the top and bottom padding
    // to the text view height; if the new bounding height would be
    // less than or equal to the text view height, append the text
    if (boundingRect.size.height + padding * 2 <= textView.frame.size.height){
        return true
    }
    else {
        return false
    }
}

0
一个选项是在 shouldChangeTextInRange 委托方法中自己修改 textView,并始终返回 NO(因为您已经为其完成了工作)。
- (BOOL)textView:(UITextView *)aTextView shouldChangeTextInRange:(NSRange)aRange replacementText:(NSString*)aText
{
    NSString* oldText = aTextView.text;
    NSString* newText = [aTextView.text stringByReplacingCharactersInRange:aRange withString:aText];
    aTextView.text = newText;

    if(/*aTextView contentSize check herer for number of lines*/)
    {
        //If it's now too big
        aTextView.text = oldText;
    }
    return NO
}

我尝试过这个方法,但不幸的是它并没有起作用。在 aTextView.text = newText 之后,UITextView 的 contentSize.height 没有改变。我在 aTextView.text = newText 之前添加了一行代码 float oldContentSizeHeight = textView.contentSize.height;。在 aTextView.text = newText 之后,即使 newText 在 UITextView 中显示为换行,contentSize.height 仍然与 oldContentSizeHeight 相同。我猜测 contentSize 将在 textViewDidChange: 后进行调整。但仍然感谢您的帮助 :) - Calvin Wu

0

正如我在这里的回答中提到的,我建议不要使用shouldChangeCharactersInRange:,因为它是在文本实际更改之前调用的。

使用textViewDidChangeMethod:更有意义,因为它是在文本实际更改后调用的。从那里,您可以轻松决定下一步该做什么。


谢谢!这真的解决了我的问题! - Calvin Wu

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