Курсор NSTextView не появляется при наборе текста в macOS 10.14 - PullRequest
0 голосов
/ 08 октября 2018

Я наблюдаю странную проблему в MacOS 10.12 Mojave с NSTextView.

The cursor is not appearing when I delete or insert text.

Я изменяю атрибуты textStorage в didChangeText() следующим образом:

self.textStorage?.beginEditing()

ARTokenManager.getToken(text: text, language: language) { (tokens) in
    // This line reset the attributes
    // If I remove it, the cursor appear properly
    // But the attributes are conserved 
    self.textStorage?.setAttributes([NSAttributedString.Key.font: self.font!,
                                     NSAttributedString.Key.foregroundColor: self.defaultTextColor], range: range)
    for token in tokens {
        let attributeRange = NSRange(location: token.range.location + range.location, length: token.range.length)
        self.textStorage?.addAttributes(token.attributes, range: attributeRange)
    }
}

self.textStorage?.endEditing()

Когда я удаляю метод setAttributes, все работает как положено, но я не могу объяснить, почему,Возможно, я сбрасываю атрибуты неправильно.Эта проблема работает только с Мохаве.

У кого-то есть такая же проблема или может объяснить мне, что я делаю не так?

Спасибо.

Ответы [ 2 ]

0 голосов
/ 08 апреля 2019

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

NSTextStorage

Для этого я использовал подкласс NSTextStorage.Здесь будет выполнена вся работа с синтаксисом.NSTextStorage не ориентирован на протокол, поэтому вы должны переопределить метод самостоятельно, как указано в документации Apple:

class SyntaxTextStorage: NSTextStorage {

    private var storage: NSTextStorage

    override var string: String {
        return storage.string
    }

    override init() {
        self.storage = NSTextStorage(string: "")
        super.init()
    }

    override func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key : Any] {
        return storage.attributes(at: location, effectiveRange: range)
    }

    override func replaceCharacters(in range: NSRange, with str: String) {
        beginEditing()
        storage.replaceCharacters(in: range, with: str)
        edited(.editedCharacters, range: range, changeInLength: str.count - range.length)
        endEditing()
    }

    override func setAttributes(_ attrs: [NSAttributedString.Key : Any]?, range: NSRange) {
        beginEditing()
        storage.setAttributes(attrs, range: range)
        edited(.editedAttributes, range: range, changeInLength: 0)
        endEditing()
    }

}

Это базовое значение для создания текстового хранилища.

NSTextStorage + NSTextView

Следующим шагом является установка хранилища текста в вашем textView.Для этого вы можете использовать метод replaceTextStorage(), доступный в textView layoutManager.

class SyntaxTextView: NSTextView {

    var storage: SyntaxTextStorage!

    override func awakeFromNib() {
        super.awakeFromNib()
        configureTextStorage()
    }

    private func configureTextStorage() {
        storage = SyntaxTextStorage()
        layoutManager?.replaceTextStorage(storage)
    }

}

Синтаксис

Последний шаг - выполнить синтаксическую работу.Стоимость процессора этого процесса очень высока.Есть много способов сделать это, чтобы получить лучшие выступления.Я предлагаю вам реализовать класс, который будет возвращать вам список NSAttributedString и NSRange.Работа с хранилищем текста должна заключаться только в применении стиля к вашему тексту.Лично я использовал метод processEditing для анализа текста:

override func processEditing() {
    super.processEditing()
    syntaxCurrentParagraph()
}

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

private func syntax(range: NSRange, completion: ((_ succeed: Bool) -> Void)? = nil) {
    guard range.length > 0 else {
        completion?(true)
        return
    }

    // Save your data to do the job in background
    let currentString = self.string
    let substring = currentString.substring(range: range)
    let mutableAttributedString = NSMutableAttributedString(string: substring, attributes: NSAttributedString.defaultAttributes as [NSAttributedString.Key : Any])

    DispatchQueue.global(qos: .background).async {
        ARSyntaxManager.tokens(text: substring, language: self.language) { (tokens) in
            // Fill you attributed string
            for token in tokens {
                mutableAttributedString.addAttributes(token.attributes, range: token.range)
            }

            DispatchQueue.main.async {
                // Check if there is no change
                guard self.string.count == currentString.count else {
                    completion?(false)
                    return
                }

                completion?(true)

                // Apply your change
                self.storage.replaceCharacters(in: range, with: mutableAttributedString)
                self.displayVisibleRect()
            }
        }
    }
}

Вот и все.Надеюсь, это поможет некоторым из вас.

0 голосов
/ 18 октября 2018

Я нашел решение.Я должен использовать метод didProcessEditing в NSTextStorageDelegate вместо didChangeText.

...