Swift | 3种电子书文本分页算法

TextKit

推荐这种,计算较为准确,效率也较高。

func calculatedBookPages(byContent content: String) -> [String] {
        let config = configuration
        let dimension = config.dimension
        
        func slicedText(from: Int, length: Int) -> String {
            var len = length
            let remainder = content.count - from
            if length > remainder {
                len = remainder
            }
            return content.slicing(from: from, length: len) ?? "Slicing Error"
        }
        
        var pagedTexts: [String] = []
        
        let attrText = attributedBookContent("我")
        let storage = NSTextStorage(string: content, attributes: attrText.attributes)
        let layoutManager = NSLayoutManager()
        storage.addLayoutManager(layoutManager)

        while true {
            let container = NSTextContainer(size: dimension.contentSize)
            container.lineBreakMode = .byWordWrapping
            container.lineFragmentPadding = 0
            layoutManager.addTextContainer(container)
            
            let range = layoutManager.glyphRange(for: container)
            if range.length <= 0 {
                break
            }
            
            pagedTexts.append(slicedText(from: range.location, length: range.length))
        }
        
        return pagedTexts
    }
extension NSAttributedString {
    typealias ParagraphStyle = (NSMutableParagraphStyle)->Void
    static func with(text: String,
                     font: UIFont? = Font.System.p14,
                     color: UIColor? = Color.black_2B2D42,
                     paragraph: ParagraphStyle? = nil,
                     attrs: [Key: Any]? = nil) -> NSAttributedString
    {
        var attributes: [Key: Any] = [:]
        if let handler = paragraph {
            let pg = NSMutableParagraphStyle()
            handler(pg)
            attributes[Key.paragraphStyle] = pg
        }
        if let font = font {
            attributes[Key.font] = font
        }
        if let color = color {
            attributes[Key.foregroundColor] = color
        }
        if let attrs = attrs {
            attrs.forEach {
                attributes[$0] = $1
            }
        }
        return .init(string: text, attributes: attributes)
    }
}

func attributedBookContent(_ text: String) -> NSAttributedString {
        let config = configuration
        return .with(text: text, font: config.font.uiFont, color: config.color.textColor, paragraph: {
            $0.lineHeightMultiple = config.paragraph.lineHeight
            $0.paragraphSpacingBefore = config.font.size
        }, attrs: [
            NSAttributedString.Key.kern: config.paragraph.wordSpacing
        ])
    }

加法

{
let length = content.count
        var idx = 0
        
        while true
        {
            if endIdx >= (length-beginIdx) {
                endIdx -= 1
                pagedTexts.append(rangedText)
                break
            }
            
            var subText = rangedText
            let attrText = attributedBookContent(byText: subText)
            let size = attrText.boundingRect(with: .init(width: W, height: .infinity), options: [.usesFontLeading, .usesLineFragmentOrigin], context: nil).size
            let h = size.height
            if h >= H {
                endIdx -= 1
                subText = rangedText
                pagedTexts.append(subText)
                
                beginIdx = idx
                endIdx = 1
            }
            else{
                idx += 1
                endIdx += 1
            }
        }
        
        print("calculate end : \(Date().timeIntervalSince1970)")
        
        return pagedTexts
}

减法

func calculatedBookPages(byContent content: String) -> [String] {
        let config = configuration
        let dimension = config.dimension
        let W = dimension.contentSize.width
        let H = dimension.contentSize.height
        
        print("calculate begin : \(Date().timeIntervalSince1970)")
        
        
        let text = attributedBookContent(byText: "中")
        let pointSize = text.yy_font!.pointSize
        let h = text.yy_lineHeightMultiple * pointSize
        let w = pointSize + CGFloat(text.yy_kern!.floatValue) * Lets.screenScale
        let count = content.count
        
        func slicedText(from: Int, length: Int) -> String {
            var len = length
            let remainder = content.count - from
            if length > remainder {
                len = remainder
            }
            return content.slicing(from: from, length: len) ?? "Slicing Error"
        }
        
        var pagedTexts: [String] = []
        var begin = 0
        let colums = Int(W / w)
        let lines = Int(ceil(H / h))
        let pageLength = colums * lines
        var stepLength = pageLength
        var subText = ""
        while true {
            subText = slicedText(from: begin, length: stepLength)
            let label = Label()
            label.attributedText = attributedBookContent(byText: subText)
            let height = label.sizeThatFits(.init(width: W, height: .infinity)).height
            if height > H {
                stepLength -= 1
            }
            else {
                begin += stepLength
                pagedTexts.append(subText)
                stepLength = pageLength
            }
            if begin >= count {
                break
            }
        }

        return pagedTexts
}

你可能感兴趣的:(Swift | 3种电子书文本分页算法)