如何将 UITextView 变成 "Pages" 就像 E-reader

How to Turn a UITextView into "Pages" like an E-reader

我有一个包含很长段落的文本视图。我不想让用户滚动以继续阅读,而是希望将文本分成几页。

屏幕将尽可能占据全部文本,然后用户可以单击下一页或上一页。换句话说,市场上任何e-reader的行为。

我真的不知道从哪里开始。我假设我必须做一些事情:

  1. 获取将进入文本视图的字符串的字符数。
  2. 获取用户文本视图在其设备上的视图宽度和高度
  3. 根据用户的设备尺寸计算字符数中有多少个字符可以显示在文本视图上。
  4. 无论我想出什么公式,都可以根据这个动态选择文本量。

let count = str.count

let userWidth = UIScreen.main.bounds.size.width

let userHeight = UIScreen.main.bounds.size.height

但是,我想不出任何可行的方法,甚至这似乎也不是正确的路径。

关于如何开始创建电子reader 翻页功能有什么建议吗?如果这样更容易,我可以使用 UIWebView 而不是 UITextView。

谢谢!

您的方向是正确的,但您的实现仅适用于等宽字体(Menlo、Courier 等),因为比例字体(Helvetica、Times 等)的字母宽度因字母而异。此外,您的计划会使文本在单词中间中断,这可能不是最佳选择。

TextKit 提供的工具可以让您更轻松地完成这一切。如果你想做标准的分页,在单词边界处换行而不是在单词边界内换行,你可以使用 NSLayoutManager 为你做这件事。

一些注意事项:

您不能使用屏幕宽度来准确计算视图的大小,因为您需要考虑安全区域和 UITextView 的 textContainerInset。

我会把分页留给你。您可以通过从字符串中修剪前一页来计算向上的页面,或者简单地向前或向后移动范围。

func stringThatFitsOnScreen(originalString: String) -> String? {
    // the visible rect area the text will fit into
    let userWidth  = textView.bounds.size.width - textView.textContainerInset.right - textView.textContainerInset.left
    let userHeight = textView.bounds.size.height - textView.textContainerInset.top - textView.textContainerInset.bottom
    let rect = CGRect(x: 0, y: 0, width: userWidth, height: userHeight)

    // we need a new UITextView object to calculate the glyphRange. This is in addition to
    // the UITextView that actually shows the text (probably a IBOutlet)
    let tempTextView = UITextView(frame: self.textView.bounds)
    tempTextView.font = textView.font
    tempTextView.text = originalString

    // get the layout manager and use it to layout the text
    let layoutManager = tempTextView.layoutManager
    layoutManager.ensureLayout(for: tempTextView.textContainer)

    // get the range of text that fits in visible rect
    let rangeThatFits = layoutManager.glyphRange(forBoundingRect: rect, in: tempTextView.textContainer)

    // convert from NSRange to Range
    guard let stringRange = Range(rangeThatFits, in: originalString) else {
        return nil
    }

    // return the text that fits
    let subString = originalString[stringRange]
    return String(subString)
}

有更复杂的方法来混合和匹配文本视图、布局管理器和文本容器。当你掌握了简单的东西时,你可以调查它。