Изменение NSTextStorage приводит к перемещению точки вставки в конец строки
У меня есть NSTextView
подкласс, действующий как его NSTextStorage
делегировать. Я пытаюсь сделать 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];
Это добавит мой результат просто отлично, но проблема в том, как уже упоминалось, точка вставки скачет до конца.
Я пробовал:
- Завершение вышеупомянутых звонков в
[textStorage beginEditing]
а также-endEditing
, - Сохранение диапазона выделения (т. Е. Точки вставки) перед изменением хранилища текста, чтобы впоследствии можно было его сбросить, но без кубиков.
Я делаю это правильно? Я пытаюсь сделать это наименее хакерским способом, и я также не уверен, является ли это идеальным местом для моего анализа / выделения. Документы заставляют меня поверить в это, но, возможно, это неправильно.
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];
}
В итоге мне пришлось сбросить точку вставки вручную здесь. Я все еще хотел бы выяснить, почему это необходимо, но, по крайней мере, это похоже на взлом.