Trying to print NSAttributedString via many UITextViews causes crash

My Mac app uses many

NSTextView
s just fine for printing a long string, but trying to do the same on iOS with many
UITextView
s and associated
UIViewPrintFormatter
s just causes a crash.
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(
  0 CoreFoundation 0x00007fff23c7127e __exceptionPreprocess + 350
  1 libobjc.A.dylib 0x00007fff513fbb20 objc_exception_throw + 48
  2 CoreFoundation 0x00007fff23d03ab1 _CFThrowFormattedException + 194
  3 CoreFoundation 0x00007fff23b83bf9 -[__NSArrayM objectAtIndex:] + 169
  4 UIKitCore 0x00007fff48029503 -[UITextViewPrintFormatter rectForPageAtIndex:] + 86
  5 UIKitCore 0x00007fff4806bc79 __57-[UIPrintPageRenderer drawPrintFormatter:forPageAtIndex:]_block_invoke + 41
  6 UIKitCore 0x00007fff4806bcf9 __57-[UIPrintPageRenderer drawPrintFormatter:forPageAtIndex:]_block_invoke.43 + 29
  7 libdispatch.dylib 0x000000010e4f6d48 _dispatch_client_callout + 8
  8 libdispatch.dylib 0x000000010e505b24 _dispatch_async_and_wait_invoke + 175
  9 libdispatch.dylib 0x000000010e4f6d48 _dispatch_client_callout + 8
  10 libdispatch.dylib 0x000000010e504de6 _dispatch_main_queue_callback_4CF + 1500
  11 CoreFoundation 0x00007fff23bd4049 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
  12 CoreFoundation 0x00007fff23bceca9 __CFRunLoopRun + 2329
  13 CoreFoundation 0x00007fff23bce066 CFRunLoopRunSpecific + 438
  14 GraphicsServices 0x00007fff384c0bb0 GSEventRunModal + 65
  15 UIKitCore 0x00007fff48092d4d UIApplicationMain + 1621
  16 myApp 0x000000010e1ca61b main + 75
  17 libdyld.dylib 0x00007fff5227ec25 start + 1
  18 ??? 0x0000000000000001 0x0 + 1
)
Here's the code:
class ViewController: UIViewController {

    override func viewDidAppear(_ animated: Bool) {
        let printController = UIPrintInteractionController.shared
        let printPageRenderer = PrintPageRenderer()
        printController.printPageRenderer = printPageRenderer
        printController.present(animated: true)
    }

}

class PrintPageRenderer: UIPrintPageRenderer {

    let layoutManager = NSLayoutManager()
    let textStorage = NSTextStorage(string: "fhdjksalhfj dskla fjf")
    var textViews = [UITextView]()

    override func prepare(forDrawingPages range: NSRange) {
        DispatchQueue.main.sync {
            for (i, textView) in textViews.enumerated() {
                let printFormatter = textView.viewPrintFormatter()
                addPrintFormatter(printFormatter, startingAtPageAt: i)
            }
        }
    }

    override var numberOfPages: Int {
        textStorage.addLayoutManager(layoutManager)
        let size = CGFloat(50)
        for _ in 0..<2 {
            let textContainer = NSTextContainer(size: CGSize(width: size, height: size))
            layoutManager.addTextContainer(textContainer)
            let textView = UITextView(frame: CGRect(x: 0, y: 0, width: size, height: size), textContainer: textContainer)
            textViews.append(textView)
        }
        return textViews.count
    }

}

My goal is printing a document from an attributed string and showing a custom header and footer on each page.

Just a hint.


Looking at doc for

addPrintFormatter(_:startingAtPageAt:)


says that

pageIndex starts at zero.

Are you sure that

addPrintFormatter(printFormatter, startingAtPageAt: i)


Check if 1 is valid:

   override func prepare(forDrawingPages range: NSRange) {
        DispatchQueue.main.sync {
            for (i, textView) in textViews.enumerated() {
                let printFormatter = textView.viewPrintFormatter()
                print("Before addPrintformatter")
                addPrintFormatter(printFormatter, startingAtPageAt: i)
                print("After addPrintformatter")

            }
        }
    }


And report what you get on log.


PS: you duplicated the post. Delete or close https://forums.developer.apple.com/thread/127590

I'm not sure that I understand. textViews.enumerated() also starts with index 0.

OK, my mistake, I read 1 where it was i.


What is the print results ? Do you see all Before and after ?

Should even add an index for better assessing

                print("Before addPrintformatter", i) 
                addPrintFormatter(printFormatter, startingAtPageAt: i) 
                print("After addPrintformatter", i)


But problem could well be an index issue elsewhere.

Need to find which array is accessed with index > 0 but it is just a one item array (or even empty one ?)

The textViews array is built in the numberOfPages getter, its length is 2 in this example. The crash doesn't happen if instead of `for _ in 0..<2` I write `for _ in 0..<1`.

Could you show the print results, that will help me understand.


And show the full getter.

Before addPrintformatter 0

After addPrintformatter 0

Before addPrintformatter 1

After addPrintformatter 1

2020-01-08 10:38:23.070823+0100 problem2[40895:756132] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'

...

The numberOfPages getter is shown in the original post.

Did you ever resolve this? I'm having exactly the same problem, except that the index is different in my error:

index 18446744073709551615 beyond bounds [0 .. 0]

(Which I believe is -1 equivalent in an unsigned integer form.)

My stack trace is identical to yours as far as I can tell.

It actually displays the preview in the print panel, and then crashes.

If I only attempt to print a single item it succeeds, but crashes when I try to print more than one item.

However, if I print multiple items all on the one page (startingAtPage: 0 for all of them), it prints successfully and does NOT crash (but then of course all the items are printed overlapping on top of each other on a single page and looks terrible).

Ignore my previous post. Mine was caused by a work around to a crash bug that appears to have since been fixed, such that now the work around causes a similar crash. I wonder if perhaps your issue may have been caused by the same bug, and has since disappeared in more recent versions of the OS?

The work around was this:

`

override func drawPrintFormatter(_ printFormatter: UIPrintFormatter, forPageAt pageIndex: Int) {

	//  WORK AROUND A CRASH BUG WHEN THERE ARE MULTIPLE UIPrintFormatters and pages

	//  See:  https://forums.developer.apple.com/thread/30009

	var index = pageIndex

	if let printFormatter = printFormatter as? UIViewPrintFormatter {

		index = pageIndex - printFormatter.startPage;

	}

	return super.drawPrintFormatter(printFormatter, forPageAt:index);

}

`

Thank you for the workaround. Yes, this issue was solved, according to the message I got in Feedback Assistant, with iOS 14.5.1.

Trying to print NSAttributedString via many UITextViews causes crash
 
 
Q