ios - 拆分字符串以适应大小的算法

标签 ios swift string algorithm

我有一个很长的字符串,我们称之为故事,我从数据库中获取这个故事,所以我不知道它有多长。

我想在 View 中显示这个故事,但是如果故事太长以至于不适合一个 View 怎么办?

我不想调整字体大小,因为这可能是一个很长的故事,把字体变小并不是一个好的解决方案。

所以我想将 Story 分成多个 View ,通过传递 Story 并将 Story 分离为 array of String 数组中的每个项目都可以放在一个 View 中。

这是代码,也许它给了你我正在尝试做的事情的提示:

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 个 View ,则需要 20 秒或更长时间。

我不擅长算法,所以我需要一个击球手或提示来让它执行得更快。

任何编程语言的提示,我非常感谢。

最佳答案

我曾多次使用它,例如实现电子书阅读器,您希望页面向右流动。

您发布的代码基本上是正确的,但速度很慢,因为它必须在您测量它的大小时逐字逐句地重新呈现所有内容(假设您的函数 isContentFit() 占用了您的大部分时间)。如果你想优化这个,你必须粗略地猜测每个字母的大小,并在开始渲染和测量之前估计你的单词有多少可以放在一页上。您还可以延迟渲染/测量即将到来的页面,直到它们需要显示之前。

另一个问题也可能是您将字符串拆分为单词然后将它们一个接一个地连接起来的方式。如果字符串很大,这也可能很慢且耗时。在这里,仅搜索原始字符串并计算字母和空格的数量,然后在知道将字符串切入页面的位置时使用字符串切片也会有好处。

另一个我曾多次成功使用的解决方案是使用 webview 和样式化您的文本,以便它显示在列中,例如通过使用如下示例中的样式:https://www.w3schools.com/cssref/tryit.asp?filename=trycss3_column-width Web 呈现引擎的功能类似于您的代码,但速度非常快。

在 iOS 上,您应该使用 css 属性 -webkit-column-width(因为 iOS 上的 webviews 基本上像在 Safari 中一样呈现)并将 -webkit-column-width 设置为您的 webview 的宽度,然后呈现整个文本到 WebView 中。结果将是您一次看到一个“页面”,并且可以向右滚动以查看下一页。

如果你想在此之上使用页面 Controller 或其他一些滚动控件,你必须注入(inject)一些 css/javascript 魔法,我必须承认这并不容易,但可以做到。

这是一个在 webview 中显示的示例 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>

关于ios - 拆分字符串以适应大小的算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55927092/

相关文章:

android - Firemonkey Mobile 中的从右到左的双向语言

swift - Firestore 未将数据加载或存储到 db swift

swift map (_ :) extension for Set() ?

java - Android/Java 附加字符串 + int

python - 如何以 Pythonic 方式将 "ABCD…"转换为 "CDEF…"?

ios - 在App Purchase中可以正常使用,但是付款成功后我无法更改UIButton图像

ios - 多次调用 NSManagedObjectContextDidSaveNotification

ios - 代码优化(2行代码合并为1行)

ios - 如何从 Today View Widget 引用 AppDelegate?

java - 将带有嵌套字符的字符串转换为java中特定的json格式