Search code examples
iosswiftnslayoutmanagernstextstorage

NSLayoutManager numberOfGlyphs always showing 0, Swift


I am trying to create a multipage PDF with UITextView on each page with the required attributed text. but When I am hitting a problem once the app is Archived and distributed via TestFlight for testing.

Below is a my sample codes which I used to generate the multi pages,

var textStorage = NSTextStorage()
textStorage = NSTextStorage(attributedString: attString)

let layoutManager = NSLayoutManager()

textStorage.addLayoutManager(layoutManager)

var pageSize = CGRect(x: 44, y: 108, width: 507, height: 690)
var lastGlyph = 0

while lastGlyph < layoutManager.numberOfGlyphs {
     let textContainer = NSTextContainer()
     let background = UINib(nibName: "background", bundle: nil).instantiate(withOwner: nil, options: nil)[0]
     background.frame = pageRect
     textContainer.size = subsequentPageSize.size
     layoutManager.addTextContainer(textContainer)
     let textView = UITextView(frame: pageSize, textContainer: textContainer)
     pageSize.origin.x += pageSize.width
     background.addSubview(textView)
     context.beginPage()
     background.layer.render(in: UIGraphicsGetCurrentContext()!)

     lastGlyph = NSMaxRange(layoutManager.glyphRange(for: textContainer))
}

This works perfectly fine if run in the simulator or on device when built from Xcode but as soon as the app is distributed the layoutManager.numberOfGlyphs always returns 0 even if I print() the layoutmanager it shows,

    <NSLayoutManager: 0x7ff313c9f6d0>
    0 containers, text backing has 57 characters
    Currently holding 57 glyphs.
    Glyph tree contents:  57 characters, 57 glyphs, 1 nodes, 64 node bytes, 64 storage bytes, 128 total bytes, 2.25 bytes per character, 2.25 bytes per glyph
    Layout tree contents:  57 characters, 57 glyphs, 0 laid glyphs, 0 laid line fragments, 1 nodes, 64 node bytes, 0 storage bytes, 64 total bytes, 1.12 bytes per character, 1.12 bytes per glyph, 0.00 laid glyphs per laid line fragment, 0.00 bytes per laid line fragment'.

Have I missed something silly or is there a bug that I am not aware of? I cannot for the life of me understand why it is not working!

Appreciate ay help that could be given.


Solution

  • I eventually found a solution in the NSTextStorage subclassing notes which state,

    The NSTextStorage class implements change management (via the beginEditing() and endEditing() methods), verification of attributes, delegate handling, and layout management notification. The one aspect it does not implement is managing the actual attributed string storage, which subclasses manage by overriding the two NSAttributedString primitives...

    So I have changed my code and added textStorage.beginEditing() and textStorage.endEditing() to the beginning and end of the entire sequence as follows and it now works once the project achieves as well as built directly to device or simulator from Xcode.

    var textStorage = NSTextStorage()
    textStorage.beginEditing()
    if attribString != nil {
        textStorage = NSTextStorage(attributedString: attribString)
    } else {
        textStorage = NSTextStorage(string: string)
    }
    
    let layoutManager = NSLayoutManager()
    textStorage.addLayoutManager(layoutManager)
    
    var pageSize = CGRect(x: 44, y: 108, width: 507, height: 690)
    var lastGlyph = 0
    while lastGlyph < layoutManager.numberOfGlyphs {     
        let textContainer = NSTextContainer()     
        let background = UINib(nibName: "background", bundle: nil).instantiate(withOwner: nil, options: nil)[0]     
        background.frame = pageRect     
        textContainer.size = subsequentPageSize.size     
        layoutManager.addTextContainer(textContainer)     
        let textView = UITextView(frame: pageSize, textContainer: textContainer)     
        background.addSubview(textView)     
        context.beginPage()     
        background.layer.render(in: UIGraphicsGetCurrentContext()!)    
        lastGlyph = NSMaxRange(layoutManager.glyphRange(for: textContainer))
    }
    textStorage.endEditing()