使 UITextView 的一部分不可删除

Posted

技术标签:

【中文标题】使 UITextView 的一部分不可删除【英文标题】:Make portion of UITextView undeletable 【发布时间】:2012-02-25 14:17:59 【问题描述】:

我有一个 UITextView,需要使特定部分不可删除。它是视图文本的前 10 个字符。

我只是想要这样,如果用户点击键盘上的删除键,它会在到达第 10 个字符时停止。

编辑

让我更详细一点。

假设前缀是“123456789:”。我希望能够在此前缀之后的任何位置键入,但它根本无法编辑,所以 '123456789:' 根本不应该被更改。 Fichek 的回答完美地做到了这一点,但是前缀并不总是存在,那么我如何检测它何时不在文本视图中?我以为 if 语句可以做到这一点,但似乎没有。

【问题讨论】:

【参考方案1】:

你可以使用委托方法textView:shouldChangeTextInRange:replacementText:来告诉文本视图是否接受删除。

正如文档所说:

范围:当前选择范围。如果范围的长度为 0,则范围反映当前插入点。 如果用户按下 Delete 键,则范围的长度为 1,并且一个空字符串对象替换该单个字符

编辑

这是一个用户不能删除前十个字符的实现。但是他可以在那里插入字符。

- (BOOL)textView:(UITextView *)textView shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

    if (range.length==1 && string.length == 0) 
        // Deleting text
        if (range.location <= 9) 
            return NO;
        
    
    return YES;

这是一个他根本无法修改前十个字符的实现。

- (BOOL)textView:(UITextView *)textView shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

    if (range.location <= 9) 
        return NO;
    
    return YES;

【讨论】:

好的,我看到我需要从范围位置 0 和长度 10 停止字符以不被删除,但我不确定如何实际执行此操作。一个简单的例子将不胜感激。 如果有20个字符,用户可以删除第一个字符吗?或者他只能删除索引为 10 或更多的字符? 假设文本是“123456789:这是一串数字”。我希望前 10 个字符不可删除,因此不能删除 '123456789:'。 非常感谢您发布示例!我已经尝试过您的第二种方法,因为我根本不希望更改那些第一个字符,但是它使我处于无法删除(很好)但也无法添加任何字符的状态。与此相关,您如何检测是否已点击删除键? @JoshKahane - 抱歉,没有看到您关于检测删除键的问题。这是使用条件:if (range.length==1 &amp;&amp; string.length == 0) 完成的,这就是我以粗体引用的文档所说的内容。【参考方案2】:

sch 的最后一次编辑给出了一个不错的答案,但我想提供一种更灵活的方法。

您必须牢记复制/粘贴系统。用户可能会选择文本字段中的所有文本并尝试粘贴可能完全可以接受的整个值,但if (range.location &lt;= 9) return NO; 会拒绝它。我这样做的方法是将一个字符串放在一起,该字符串将是成功编辑的结果,然后检查该字符串是否以您想要的前缀开头。

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string

    NSString *resultString = [textField.text stringByReplacingCharactersInRange:range withString:string];
    NSLog(@"resulting string would be: %@", resultString);
    NSString *prefixString = @"blabla";
    NSRange prefixStringRange = [resultString rangeOfString:prefixString];
    if (prefixStringRange.location == 0) 
        // prefix found at the beginning of result string
        return YES;
    
    return NO;

编辑:如果要检查文本字段中的当前字符串是否以前缀开头,可以同样使用rangeOfString:

NSRange prefixRange = [textField.text rangeOfString:prefixString];
if (prefixRange.location == 0) 
    // prefix found at the beginning of text field

【讨论】:

是的,我知道这一点,但我还不知道规格是什么。无论如何+1 :) 请检查我的编辑。很好的答案,但是当前缀不存在时,它仍然不会让我编辑那些第一个字符。 "但是前缀并不总是存在,那么我如何检测它何时不在文本视图中?"不确定我理解正确,但您可以使用相同的检查 (rangeOfString:) 来查看文本文件本身是否以前缀开头。【参考方案3】:

对于一个完整的解决方案,您需要处理多种情况,包括可能从不可编辑的部分开始并扩展到用户可以编辑的部分的剪切和粘贴操作。我添加了一个变量来控制包含不可编辑部分但扩展到可编辑部分的操作是否有效。如果有效,则调整范围以仅影响可编辑部分。

// if a nil is returned, the change is NOT allowed
- (NSString *)allowChangesToTextView:(UITextView *)textView inRange:(NSRange)changeRange withReplacementText:(NSString *)text
                       immutableUpTo:(NSInteger)lastReadOnlyChar adjustRangeForEdits:(BOOL)adjustRangeForEdits;

    NSString *resultString = @"";

    NSString *currentText = textView.text;
    NSInteger textLength = [currentText length];

    // if trying to edit the first part, possibly prevent it.
    if (changeRange.location <= lastReadOnlyChar)
    
        // handle typing or backspace in protected range.
        if (changeRange.length <= 1)
        
            return nil;
        

        // handle all edits solely in protected range
        if ( (changeRange.location + changeRange.length) <= lastReadOnlyChar)
        
            return nil;
        

        // if the user wants to completely prevent edits that extend into the
        // read only substring, return no
        if (!adjustRangeForEdits)
        
            return nil;
        

        // the range includes read only part but extends into editable part.
        // adjust the range so that it does not include the read only portion.

        NSInteger prevLastChar = changeRange.location + changeRange.length - 1;
        NSRange newRange =  NSMakeRange(lastReadOnlyChar +  1, prevLastChar - (lastReadOnlyChar +  1) + 1);

        resultString = [textView.text stringByReplacingCharactersInRange:newRange withString:text];

        return resultString;
    

    // the range does not include the immutable part.  Make the change and return the string
    resultString = [currentText stringByReplacingCharactersInRange:changeRange withString:text];
    return resultString;

这是从文本视图委托方法中调用它的方式:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text

    // did the user press enter?
    if ([text isEqualToString:@"\n"])
    
        [textView resignFirstResponder];
        return NO;
    

    NSInteger endOfReadOnlyText = [self.spotTextLastSet length] - 1;
    NSString *newText = [self allowChangesToTextView:textView inRange:range withReplacementText:text
                                       immutableUpTo:endOfReadOnlyText adjustRangeForEdits:YES];

    if (newText == nil)
    
        // do not allow!
        [TipScreen showTipTitle:@"Information" message:@"The first part of the text is not editable.  Please add your comments at the end."
                      ForScreen:@"editWarning"];
        return NO;
    

    // lets handle the edits ourselves since we got the result string.
    textView.scrollEnabled = NO;
    textView.text = newText;


    // move the cursor to change range start + length of replacement text
    NSInteger newCursorPos = range.location + [text length];
    textView.selectedRange = NSMakeRange(newCursorPos, 0);
    textView.scrollEnabled = YES;

    return NO;

【讨论】:

以上是关于使 UITextView 的一部分不可删除的主要内容,如果未能解决你的问题,请参考以下文章

自 iOS 7 中的删除(退格)操作以来,我如何才能不接收 UITextView 委托消息?

从UITextView创建PDF

UITextView 上的 UITableViewCell 选择

可变高度 UITextView

UITextView 中不必要的换行符

从 UITextView 的菜单中选择和删除文本时崩溃