Search code examples
iosswiftprintinguiedgeinsetsuiprintpagerenderer

swift UIPrintPageRenderer printableRect.size.width is always zero (0)


I'm using a UIActivityViewController to print (amongst other activities). So I pass it an instance of my custom subclass of UIPrintPageRenderer, for which the relevant code is below.

In essence, I want to print two multi-line attributed strings, side-by-side, like two columns (eventually, I'd like to have one embedded in the other and wrapped around, but lets not get ahead of ourselves here). The right side text view must be a fixed size according to its content (its subclass overrides sizeToFit() to achieve this). The left side text view should fill the remaining width.

So I'm using to UITextView instances, populated with the attributed strings, and assigning their respective .viewPrintFormatter()` outputs as the UIPrintFormatters to the UIPrintPageRenderer.

This partially works. Both attributed strings are printed on the page.

However, they are printed on top of each other, both at the left edge of the page.

My attempts to use UIEdgeInsets to restrict their printing fails, unless I hard code values. It appears that this is because I get 0 (zero) when querying the printableRect.size.width.

Why is my UIPrintPageRendere's printableRect always zero width?

What is the correct way to achieve side-by-side printing of two multi-line attributed strings?

class CustomPrintPageRenderer: UIPrintPageRenderer {
    let leftTextView = UITextView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    let rightTextView = IngredientsTextView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))

    init(_ thing: Thing) {
        super.init()
        addThing(thing)
    }

    func addThing(_ thing: Thing) {
        //  Do some stuff here to populate the two text views with attributed strings
        //  ...
        //  ...
        rightTextView.sizeToFit()
        let leftPrintFormatter = leftTextView.viewPrintFormatter()
        let rightPrintFormatter = rightTextView.viewPrintFormatter()
        print(paperRect.size.width)
        print(printableRect.size.width)
        rightPrintFormatter.perPageContentInsets = UIEdgeInsets(top: formatter.titleFontSize, left: printableRect.size.width - rightTextView.frame.size.width, bottom: 0, right: 0)
        leftPrintFormatter.perPageContentInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: rightTextView.frame.size.width)

        addPrintFormatter(leftPrintFormatter, startingAtPageAt: numberOfPages)
        addPrintFormatter(rightPrintFormatter, startingAtPageAt: numberOfPages)
    }
}

Solution

  • I have figured this out. It seems that the paperRect and printableRect properties are not available at init() time (which is where I was calling my addThing() from).

    I have to do this work by overriding one of the other functions, such as drawPrintFormatter() or numberOfPages().

    This works mostly as expected:

        override func drawPrintFormatter(_ printFormatter: UIPrintFormatter, forPageAt pageIndex: Int) {
            if printFormatter == rightPrintFormatter {
                printFormatter.perPageContentInsets = UIEdgeInsets(top: RecipeFormatter.titlePrintTextSize, left: printableRect.size.width - ingrWidth, bottom: 0, right: 0)
            } else if printFormatter == leftPrintFormatter {
                printFormatter.perPageContentInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: ingrWidth)
            }
            super.drawPrintFormatter(printFormatter, forPageAt: pageIndex)
        }