iosswiftperformancenstextstorage

NSTextStorage with long text inside. Performance


I have a NSTextStorage with long text inside (like 500 pages in a book and more than 9000 on a device for current font). I'm distributing this text for textcontainers this way:

let textStorageLength = defaultTextStorage?.length ?? 0
while layoutManager!.textContainer(forGlyphAt: textStorageLength - 1, 
                                   effectiveRange: nil) == nil {
  let textContainer = NSTextContainer(size: textContainerSize)
  layoutManager!.addTextContainer(textContainer)
  pagesCount += 1
}

The problem is that it takes long period of time to init all this containers etc. I already have made some improvements like changing code from using

while lastRenderedGlyph < layoutManager!.numberOfGlyphs {

and

lastRenderedGlyph = NSMaxRange(layoutManager!.glyphRange(for: textContainer))

cz it is working much more slower.

So, what other improvements can I make? On iPhone 7 it takes like 7 seconds for initing, on iPhone 5s 20 secs +

Time profiler shows, that almost all the time is getting insertTextContainer function (addTextContainer).

Any suggestions?


Solution

  • Layout manager invalidates layout on addTextContainer method call and rebuilds layout on textContainer(forGlyphAt:effectiveRange:). You can check it by setting delegate for layout manager and observing layoutManagerDidInvalidateLayout. So rather then do layout once, you are doing it 500 times - once for every added text container.

    You can add text containers in batches to reduce number of layouts, e.g.

      var lastTextConainer: NSTextContainer? = nil
      while nil == lastTextConainer {
         for _ in 1...100 {
           let textContainer = NSTextContainer(size: textContainerSize)
           layoutManager.addTextContainer(textContainer)
         }
         lastTextConainer = layoutManager.textContainer(forGlyphAt: layoutManager.numberOfGlyphs - 1, effectiveRange: nil)
      }
      let pagesCount = layoutManager.textContainers.index(of: lastTextConainer!)! + 1
    

    Empty text containers at the end you can leave or remove starting from last text container index. Enjoy!