Изменение NSTextStorage приводит к перемещению точки вставки в конец строки

У меня есть NSTextView подкласс, действующий как его NSTextStorage делегировать. Я пытаюсь сделать 2 вещи:

  1. Выделите текст в некоторых отношениях
  2. Оцените текст, а затем добавьте ответ в текстовое представление.

Я делаю это двумя разными способами, оба вызывается - (void)textStorageWillProcessEditing:(NSNotification *)notification делегат обратного вызова.

Я могу сделать подсветку синтаксиса просто отлично, но когда дело доходит до добавления моего ответа, точка вставки переходит в конец строки, и я не знаю, почему. Мой метод оценки выглядит следующим образом:

NSString *result = ..;
NSRange lineRange = [[textStorage string] lineRangeForRange:[self selectedRange]];
NSString *line = [[textStorage string] substringWithRange:lineRange];
line = [self appendResult:result toLine:line]; // appends the answer

[textStorage replaceCharactersInRange:lineRange withString:line];

Это добавит мой результат просто отлично, но проблема в том, как уже упоминалось, точка вставки скачет до конца.

Я пробовал:

  1. Завершение вышеупомянутых звонков в [textStorage beginEditing] а также -endEditing,
  2. Сохранение диапазона выделения (т. Е. Точки вставки) перед изменением хранилища текста, чтобы впоследствии можно было его сбросить, но без кубиков.

Я делаю это правильно? Я пытаюсь сделать это наименее хакерским способом, и я также не уверен, является ли это идеальным местом для моего анализа / выделения. Документы заставляют меня поверить в это, но, возможно, это неправильно.

3 ответа

Причина перемещения точки вставки

Удивительно, но я так и не нашел реального объяснения тому, почему эти предложения работают (или не работают).

Копаясь в этом, причина для точки вставки для перемещения: .editedCharacters (NSTextStorageEditedCharacters в ObjC) влияет на положение точки вставки из NSLayoutManager.processEditing(from:editedMask:...),

Если только .editedAttributes/NSTextStorageEditedAttributes отправлено, точка вставки не будет затронута. Это то, чего вы хотите достичь, когда выделите: изменить только атрибуты.

Почему выделение влияет на точку вставки

Проблема с выделением здесь заключается в том, что NSTextStorage собирает все edited вызывает во время одного прогона обработки и объединяет диапазоны, начиная с изменения, отредактированного пользователем (например, вставка при вводе), затем формируя объединение этого и всех диапазонов, сообщаемых addAttributes(_:range:), Это приводит к одному NSLayoutManager.processEditing(from:editedMask:...) позвонить - с editedMask обоих [.editedCharacters, .editedAttributes],

Итак, вы хотите отправить .editedAttributes для выделенных диапазонов, но в итоге образует союз с .editedCharacters вместо. Этот союз перемещает точку вставки waaaaaaaay дальше, чем он должен идти.

Изменение порядка в processEditing Вызов супер сначала работает, потому что менеджер макета будет уведомлен о завершении редактирования. Но этот подход все еще не работает для некоторых крайних случаев, что приводит к неправильной компоновке или покачиванию видов прокрутки при вводе очень больших абзацев.

Это верно для того, чтобы подключиться кNSTextStorageDelegateтоже кстати.

Подключитесь к обратным вызовам после того, как макет действительно завершен, чтобы вызвать подсветку вместо processEditing

Единственное решение, которое будет работать надежно на основе причин, присущих инфраструктуре Какао, - это выделение из textDidChange(_:) исключительно, т.е. после того, как обработка макета действительно была закончена. Подписка на NSTextDidChangeNotification работать так же хорошо.

Недостаток: вы должны инициировать проходы подсветки для программных изменений базовой строки, так как они не вызовут textDidChange(_:) Перезвоните.


Если вы хотите узнать больше об источнике проблемы, я добавлю свои исследования, различные подходы и подробности решения в гораздо более длинное сообщение в блоге для справки. Этот пост все еще является самостоятельным решением: http://christiantietze.de/posts/2017/11/syntax-highlight-nstextstorage-insertion-point-change/

Я знаю, что на этот вопрос уже давно дан ответ, однако у меня была точно такая же проблема. В моем NSTextStorage В подклассе я делал следующее:

- (void)processEditing {
    //Process self.editedRange and apply styles first
    [super processEditing];
}

Тем не менее, правильная вещь сделать это:

- (void)processEditing {
    [super processEditing];
    //Process self.editedRange and apply styles after calling superclass method
}

Это просто! В итоге я разбил эту проблему на 2 части. Я все еще делаю подсветку синтаксиса в результате textStorage обратный вызов делегата, но теперь я делаю свою оценку и добавляю в другом месте.

Я закончил тем, что переопределил оба -insertText: а также -deleteBackwards: (Я мог бы также сделать то же самое для -deleteForwards:, тоже). Оба переопределения выглядят следующим образом:

- (void)insertText:(id)insertString {
    [super insertText:insertString];
    NSRange selectedRange = [self selectedRange];
    [self doEvaluationAndAppendResult];
    [self setSelectedRange:selectedRange];
}

В итоге мне пришлось сбросить точку вставки вручную здесь. Я все еще хотел бы выяснить, почему это необходимо, но, по крайней мере, это похоже на взлом.

Другие вопросы по тегам