每个新行添加一个编号列表UITextView

Hor*_*ray 3 objective-c uitextview ios

每当用户输入换行符时,我都会尝试在每行的开头添加一个数字.我希望数字按顺序排列(如在有序列表中),但是使用我当前的代码,如果用户最后没有添加新行,而是在中间添加行UITextView,则会继续从它在底部的位置开始计算 - 意味着NSUInteger我做了增量并且没有考虑到用户没有在最后创建新线.

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
    if ([text isEqualToString:@"\n"])
    {
        numbered ++;
        NSString *string = [NSString stringWithFormat:@"\n%lu ", (unsigned long)numbered];
        [self insertXEveryNewLine:range :textView :string];

        return NO;
    }
    return YES;
}


- (BOOL)insertXEveryNewLine:(NSRange)place :(UITextView *)View :(NSString *)something
{
    NSRange cursor = NSMakeRange(place.location + 3, 0);

    NSMutableString *mutableT = [NSMutableString stringWithString:View.text];
    [mutableT insertString:something atIndex:place.location];
    [View setText:mutableT];
    return NO;
}
Run Code Online (Sandbox Code Playgroud)

我刚发布的代码添加了一个编号列表,每个新行增加1.现在,如果您尝试在中间添加新行,而不是在最后,它将从最后一个行号增加1,它不会从前一行号增加.例如,如果用户添加6行UITextView,则用户转到第3行并添加新行,它将在第3行之后显示#7,因为每次用户创建新行时numbered都会增加1.

编辑

在此输入图像描述

当用户在第1行之后添加新行时,我希望所有行都更新.希望这更清楚.

Lyn*_*ott 7

这个场景实际上比我预期的更复杂,因为在用户输入时动态创建编号列表,需要一个代码来处理与删除,插入,光标位置等相关的许多各种场景.但是我的答案的核心,这个代码基本上是通过将文本视图字符串分隔为"\n"分隔的"行"组件,从每行中删除当前尾随数字,按顺序重新添加适当的数字,然后重新组合字符串以返回到文本视图.

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

    // Add "1" when the user starts typing into the text field
    if (range.location == 0 && textView.text.length == 0) {

        // If the user simply presses enter, ignore the newline
        // entry, but add "1" to the start of the line.
        if ([text isEqualToString:@"\n"]) {
            [textView setText:@"1 "];
            NSRange cursor = NSMakeRange(range.location + 3, 0);
            textView.selectedRange = cursor;
            return NO;
        }

        // In all other scenarios, append the replacement text.
        else {
            [textView setText:[NSString stringWithFormat:@"1 %@", text]];
        }
    }

    // goBackOneLine is a Boolean to indicate whether the cursor
    // should go back 1 line; set to YES in the case that the
    // user has deleted the number at the start of the line
    bool goBackOneLine = NO;

    // Get a string representation of the current line number
    // in order to calculate cursor placement based on the
    // character count of the number
    NSString *stringPrecedingReplacement = [textView.text substringToIndex:range.location];
    NSString *currentLine = [NSString stringWithFormat:@"%lu", [stringPrecedingReplacement componentsSeparatedByString:@"\n"].count + 1];

    // If the replacement string either contains a new line
    // character or is a backspace, proceed with the following
    // block...
    if ([text rangeOfString:@"\n"].location != NSNotFound || range.length == 1) {

        // Combine the new text with the old
        NSString *combinedText = [textView.text stringByReplacingCharactersInRange:range withString:text];

        // Seperate the combinedText into lines
        NSMutableArray *lines = [[combinedText componentsSeparatedByString:@"\n"] mutableCopy];

        // To handle the backspace condition
        if (range.length == 1) {

            // If the user deletes the number at the beginning of the line,
            // also delete the newline character proceeding it
            // Check to see if the user's deleting a number and
            // if so, keep moving backwards digit by digit to see if the
            // string's preceeded by a newline too.
            if ([textView.text characterAtIndex:range.location] >= '0' && [textView.text characterAtIndex:range.location] <= '9') {

                NSUInteger index = 1;
                char c = [textView.text characterAtIndex:range.location];
                while (c >= '0' && c <= '9') {

                    c = [textView.text characterAtIndex:range.location - index];

                    // If a newline is found directly preceding
                    // the number, delete the number and move back
                    // to the preceding line.
                    if (c == '\n') {
                        combinedText = [textView.text stringByReplacingCharactersInRange:NSMakeRange(range.location - index, range.length + index) withString:text];

                        lines = [[combinedText componentsSeparatedByString:@"\n"] mutableCopy];

                        // Set this variable so the cursor knows to back
                        // up one line
                        goBackOneLine = YES;

                        break;
                    }
                    index ++;
                }
            }

            // If the user attempts to delete the number 1
            // on the first line...
            if (range.location == 1) {

                NSString *firstRow = [lines objectAtIndex:0];

                // If there's text left in the current row, don't
                // remove the number 1
                if (firstRow.length > 3) {
                    return  NO;
                }

                // Else if there's no text left in text view other than
                // the 1, don't let the user delete it
                else if (lines.count == 1) {
                    return NO;
                }

                // Else if there's no text in the first row, but there's text
                // in the next, move the next row up
                else if (lines.count > 1) {
                    [lines removeObjectAtIndex:0];
                }
            }
        }

        // Using a loop, remove the numbers at the start of the lines
        // and store the new strings in the linesWithoutLeadingNumbers array
        NSMutableArray *linesWithoutLeadingNumbers = [[NSMutableArray alloc] init];

        // Go through each line
        for (NSString *string in lines) {

            // Use the following string to make updates
            NSString *stringWithoutLeadingNumbers = [string copy];

            // Go through each character
            for (int i = 0; i < (int)string.length ; i++) {

                char c = [string characterAtIndex:i];

                // If the character's a number, remove it
                if (c >= '0' && c <= '9') {
                    stringWithoutLeadingNumbers = [stringWithoutLeadingNumbers stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:@""];
                } else {
                    // And break from the for loop since the number
                    // and subsequent space have been removed
                    break;
                }
            }

            // Remove the white space before and after the string to
            // clean it up a bit
            stringWithoutLeadingNumbers = [stringWithoutLeadingNumbers stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

            [linesWithoutLeadingNumbers addObject:stringWithoutLeadingNumbers];
        }

        // Using a loop, add the numbers to the start of the lines
        NSMutableArray *linesWithUpdatedNumbers = [[NSMutableArray alloc] init];

        for (int i = 0 ; i < linesWithoutLeadingNumbers.count ; i ++) {
            NSString *updatedString = [linesWithoutLeadingNumbers objectAtIndex:i];
            NSString *lineNumberString = [NSString stringWithFormat:@"%d ", i + 1];
            updatedString = [lineNumberString stringByAppendingString:updatedString];
            [linesWithUpdatedNumbers addObject:updatedString];
        }

        // Then combine the array back into a string by re-adding the
        // new lines
        NSString *combinedString = @"";

        for (int i = 0 ; i < linesWithUpdatedNumbers.count ; i ++) {
            combinedString = [combinedString stringByAppendingString:[linesWithUpdatedNumbers objectAtIndex:i]];
            if (i < linesWithUpdatedNumbers.count - 1) {
                combinedString = [combinedString stringByAppendingString:@"\n"];
            }
        }

        // Set the cursor appropriately.
        NSRange cursor;
        if ([text isEqualToString:@"\n"]) {
           cursor = NSMakeRange(range.location + currentLine.length + 2, 0);
        } else if (goBackOneLine) {
            cursor = NSMakeRange(range.location - 1, 0);
        } else {
            cursor = NSMakeRange(range.location, 0);
        }

        textView.selectedRange = cursor;

        // And update the text view
        [textView setText:combinedString];

        return NO;
    }

    return YES;
}
Run Code Online (Sandbox Code Playgroud)