Со ссылкой на ответ, опубликованный Аспери (https://stackoverflow.com/users/12299030/asperi) на Вопрос: выделите определенную часть текста в SwiftUI

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

В моем приложении, если кто-то ищет слово «блин», искомое слово будет сохранено, и когда пользователь смотрит на страницу с подробностями (скажем, рецепт), слово блин будет выделено. С этим ответом все работает хорошо, но когда рецепт превышает 32 тыс. Символов, приложение вылетает с сообщениями с превышением диапазона индекса. (конкретное сообщение об ошибке: Поток 1: EXC_BAD_ACCESS (код = 2, адрес = 0x16d43ffb4))

Вот модифицированный код из ответа на этот вопрос:

Это напечатает данные:

hilightedText(str: self.recipes.last!.recipeData!)
                        .multilineTextAlignment(.leading)
                        .font(.system(size: CGFloat( settings.fontSize )))

Очевидно, что в этом коде есть что-то большее, но, по сути, он выполняет итерацию базы данных и находит последнюю запись, содержащую «поисковое слово», и отображает здесь recipeData, которая представляет собой большую строку, содержащуюся в базе данных.

Реализовать функциональность выделенного текста:

    func hilightedText(str: String) -> Text {
        let textToSearch = searched
        var result: Text!
        for word in str.split(separator: " ") {
            var text = Text(word)
            if word.uppercased().contains(textToSearch.uppercased()) {
                text = text.bold().foregroundColor(.yellow)
            }
            //THIS NEXT LINE has been identified as the problem:
            result = (result == nil ? text : result + Text(" ") + text)
        }
        return result
    }

Я немного изменил ответ от Asperi, чтобы удовлетворить свои потребности, и все работает очень хорошо, если только я не сталкиваюсь с записью recipeData размером более 32 КБ, как указано выше.

Я попытался заменить String несколькими другими типами данных, и ничего не работает ..

Есть идеи?

Спасибо!

ОБНОВИТЬ:

После длительного обсуждения в комментариях выясняется, что коренная причина проблемы в какой-то момент, для некоторых записей я превышаю максимальный текст ("") конкатенации.

В приведенном выше коде каждое слово выделяется, оценивается и добавляется в длинную строку «result», которая в итоге выглядит следующим образом: Text("word") + Text(" ") + Text("Word") и так далее.

Это сделано, поэтому я могу легко применить цветовые атрибуты для каждого слова, но может показаться, что, как только я наберу определенное количество слов (которое меньше 32 КБ, одна запись была 22 КБ и потерпела крах), приложение вылетает.

Лео предложил https://stackoverflow.com/a/59531265/2303865 в качестве альтернативы, и мне придется попытаться реализовать это вместо.

Спасибо..

3
Lkabo 19 Июл 2020 в 03:32

2 ответа

Лучший ответ

Хм ... неожиданное ограничение ... во всяком случае - узнать что-то новое.

Хорошо, вот улучшенный алгоритм, который должен отодвинуть это ограничение далеко.

Протестировано с Xcode 12 / iOS 14. (также обновлен код в указанной теме Выделить определенную часть текста в SwiftUI)

func hilightedText(str: String, searched: String) -> Text {
    guard !str.isEmpty && !searched.isEmpty else { return Text(str) }

    var result = Text("")

    var range = str.startIndex..<str.endIndex
    repeat {
        guard let found = str.range(of: searched, options: .caseInsensitive, range: range, locale: nil) else {
            result = result + Text(str[range])
            break
        }

        let prefix = str[range.lowerBound..<found.lowerBound]
        result = result + Text(prefix) + Text(str[found]).bold().foregroundColor(.yellow)

        range = found.upperBound..<str.endIndex
    } while (true)

    return result
}
1
Asperi 19 Июл 2020 в 05:30

После долгих обсуждений в комментариях стало ясно, что я достиг максимального предела конкатенаций Text (), так что будьте осторожны, по-видимому, он есть.

Однако я понял, что мне нужно иметь разделенный текст («Word») только тогда, когда это конкретное слово требует специального форматирования (подсветка IE и т. Д.), В противном случае я могу объединить все необработанные строки вместе и отправить их как текст (» Строка слов ").

Этот подход смягчает действие, когда каждое отдельное слово отправляется как текст («Слово»), и значительно сокращает количество возвращаемых Text ().

Посмотрите код ниже, который решил проблему:

func hilightedText(str: String) -> Text {
    let textToSearch = searched
    var result = Text(" ")
    var words: String = " "
    var foundWord = false
    for line in str.split(whereSeparator: \.isNewline) {
        for word in line.split(whereSeparator: \.isWhitespace) {
            if word.localizedStandardContains(textToSearch) {
                foundWord = true
                result += Text(words) + Text(" ") + Text(word).bold().foregroundColor(.yellow)

            } else {
                if foundWord {
                    words = ""
                }
                foundWord = false
                words += " " + word
            } 
        }
        words += "\n\n"
    }
    return result + Text(" ") + Text(words)
}

extension Text {
    static func += (lhs: inout Text, rhs: Text) {
        lhs = lhs + rhs
    }
}

Он может использовать некоторую очистку, как также обсуждалось в комментариях, для разделения по пробелам и т. Д., Но это было только для того, чтобы решить проблему сбоев. Требуется дополнительное тестирование, прежде чем я назову это хорошим, но больше не будет сбоев.

ДОБАВЛЕНО: предложение использовать разделитель с помощью .isWhiteSpace сработало, но когда я собрал его обратно, все было пробелом, больше не было разрывов строк, поэтому я добавил дополнительный разделитель на разрывы строк, чтобы сохранить разрывы строк.

0
Leo Dabus 19 Июл 2020 в 04:31