Алгоритм разделения строки по размеру - PullRequest
0 голосов
/ 30 апреля 2019

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

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

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

Поэтому я хочу разделить Историю на более чем одно представление, Передав Историю и разделив Историю как array of String, каждый элемент в массиве может уместиться в одном представлении.

Это код, может быть, он даст вам подсказку, что я пытаюсь сделать:

extension String {

    /// - returns: The height that will fit Self
    func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
        return ceil(boundingBox.height)
    }

    #warning("it's so slow that i can't handle it in main thread , ( 21 second for 15 items in array )")
    /// complition contains the separated string as array of string
    func splitToFitSize(_ size : CGSize = UIScreen.main.bounds.size ,font : UIFont = UIFont.systemFont(ofSize: 17) , complition : @escaping (([String]) -> Void) )  {
        DispatchQueue.global(qos: .background).async {

            // contents contains all the words as Array of String
            var contents = self.components(separatedBy: .whitespaces)

            var values : [String] = []
            for content in contents {
                // if one word can't fit the size -> remove it , which it's not good, but i don't know what to do with it
                guard content.isContentFit(size: size , font : font) else {contents.removeFirst(); continue;}
                if values.count > 0 {
                    for (i , value) in values.enumerated() {
                        var newValue = value
                        newValue += " \(content)"
                        if newValue.isContentFit(size: size, font: font) {
                            values[i] = newValue
                            contents.removeFirst()
                            break;
                        }else if i == values.count - 1 {
                            values.append(content)
                            contents.removeFirst()
                            break;
                        }
                    }
                }else {
                    values.append(content)
                    contents.removeFirst()
                }
            }
            complition(values)
        }
    }

    /// - returns: if Self can fit the passing size
    private func isContentFit(size : CGSize, font : UIFont) -> Bool{
        return self.height(withConstrainedWidth: size.width, font: font) < size.height
    }
}

Этот код работает, но это займет так много времени, если я хочу разделить историюдля 15 просмотров требуется 20 или более секунд.

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

Просто подсказка на любом языке программирования,я очень благодарен.

1 Ответ

1 голос
/ 30 апреля 2019

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

Код, который вы разместили, по сути корректен, но он медленный, потому что он должен перерисовывать все слово в слово, когда вы измеряете его размер (при условии, что ваша функция isContentFit () занимает большую часть вашего времени). Если вы хотите оптимизировать это, вы должны сделать приблизительное предположение о размере каждой буквы и получить оценку того, сколько ваших слов может уместиться на одной странице, прежде чем начать отображать и измерять. Вы также можете отложить рендеринг / измерение следующих страниц до тех пор, пока они не отобразятся.

Другая проблема также может заключаться в том, как вы разбиваете строку на слова, а затем объединяете их по очереди. Это также может быть медленным и длительным, если строка большая. Здесь также было бы полезно просто искать исходную строку и считать буквы и пробелы, а затем использовать нарезку строк, когда вы знаете, где разрезать строку на страницы.

Еще одно решение, в котором я успешно участвовал несколько раз, - это использование веб-просмотра и стилизация текста, чтобы он отображался в столбцах, например, с использованием стилей, как в следующем примере: https://www.w3schools.com/cssref/tryit.asp?filename=trycss3_column-width Движок веб-рендеринга делает что-то похожее на ваш код, но невероятно быстро.

В iOS вы должны использовать свойство css -webkit-column-width (поскольку веб-просмотры в iOS по существу отображаются как в Safari) и установить -webkit-column-width в качестве ширины вашего веб-просмотра, а затем визуализировать весь текст в веб-просмотр. В результате вы будете видеть одну «страницу» за раз и сможете прокрутить вправо, чтобы увидеть следующую страницу.

Если вы хотите использовать контроллер страницы или какой-либо другой элемент управления прокруткой поверх этого, вы должны внедрить некоторую магию css / javascript, что непросто, я должен признать, но это может быть сделано.

Вот пример строки HTML для отображения в веб-представлении. Просто вставьте всю свою строку истории в div:

<html>
<head>
<style> 
.multicolumn {
    -webkit-column-width: 500px; /* set this to the width of your webview */
    height: 300px; /* set this to the height of your webview */
    overflow-x: scroll;
}
</style>
</head>
<body>
<div class="multicolumn">

Insert long long text here...

</div>

</body>
</html>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...