How to Initialize NSTextStorage with a String in Swift

3.2k views Asked by At

In order to break another problem down into smaller parts, I am trying to set up all the TextKit components. However, I am getting an crash after changing how I initialize NSTextStorage. For testing purposes I have simplified the project to the following:

import UIKit

class ViewController3: UIViewController {

    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var myTextView: MyTextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let container = NSTextContainer(size: myTextView.bounds.size)
        let layoutManager = NSLayoutManager()
        let textStorage = NSTextStorage(string: "This is a test")
        layoutManager.addTextContainer(container)

        //layoutManager.textStorage = textView.textStorage  // This works
        layoutManager.textStorage = textStorage  // This doesn't work

        myTextView.layoutManager = layoutManager

    }
}

class MyTextView: UIView {

    var layoutManager: NSLayoutManager?

    override func drawRect(rect: CGRect) {
        let context = UIGraphicsGetCurrentContext();

        // Enumerate all the line fragments in the text
        layoutManager?.enumerateLineFragmentsForGlyphRange(NSMakeRange(0, layoutManager!.numberOfGlyphs), usingBlock: {
            (lineRect: CGRect, usedRect: CGRect, textContainer: NSTextContainer!, glyphRange: NSRange, stop: UnsafeMutablePointer<ObjCBool>) -> Void in

            // Draw the line fragment
            self.layoutManager?.drawGlyphsForGlyphRange(glyphRange, atPoint: CGPointMake(0, 0))

        })
    }
}

It crashes at enumerateLineFragmentsForGlyphRange with an exception code of EXC_I386_GPFLT. That code isn't very explanitory. The basic problem seems to be coming down to how I am initializing NSTextStorage.

If I replace

let textStorage = NSTextStorage(string: "This is a test")
layoutManager.textStorage = textStorage

with this

layoutManager.textStorage = textView.textStorage

then it works. What am I doing wrong?

1

There are 1 answers

1
James Alvarez On BEST ANSWER

It seems the way to do things, is to add the NSLayoutManager to the NSTextStorage object, (using addLayoutManager:) rather than setting the textStorage property on the layout manager.

From Apple's documents:

This method is invoked automatically when you add an NSLayoutManager to an NSTextStorage object; you should never need to invoke it directly, but you might want to override it. If you want to replace the NSTextStorage object for an established group of text-system objects containing the receiver, use replaceTextStorage:.

Link to setTextStorage: for NSLayoutManager

Presumably something gets done in 'addLayoutManager:' which doesn't get done in setTextStorage, causing the crash.

You might also want to increase the scope of the textStorage variable, if it appears that it's getting cleared up once viewDidLoad finish.