简体   繁体   中英

Printing in Cocoa

I have made a view that pretty much fits A4 page. Now I want to print it. Note that I am not using drawRect or anything like that, just a normal view with subviews and text labels. My problem is that I have a few views on that view which I use layers to put background colors and rounded rects around the items. The subviews don't print, but all text labels print.

The _printReport is just a plain window with a view and bunch of text labels.

What am I doing wrong and how could I do this better ? I really don't want to do a drawrect but I will if I have to.

Here is the code that happens when someone prints :

- (void)printWorksheet {
    TJContact *worksheet = [[self.workSheets selectedObjects] objectAtIndex:0];
    if (worksheet == nil) return;

    _printReport = [[TJPrintReportWindowController alloc] initWithWindowNibName:@"TJPrintReportWindowController"];
    [self.printReport setCompany:self.company];
    [self.printReport setContact:worksheet];

    [self.printReport showWindow:self];
    [self.printReport becomeFirstResponder];
    [self.printReport.view becomeFirstResponder];
    NSPrintInfo* printInfo = [NSPrintInfo sharedPrintInfo];

    [printInfo setHorizontalPagination:NSFitPagination];
    [printInfo setVerticalPagination:NSFitPagination];
    [printInfo setHorizontallyCentered:YES];
    [printInfo setVerticallyCentered:YES];
    [printInfo setLeftMargin:20.0];
    [printInfo setRightMargin:20.0];
    [printInfo setTopMargin:10.0];
    [printInfo setBottomMargin:10.0];

    NSPrintOperation* printOperation = [NSPrintOperation printOperationWithView:self.printReport.view printInfo:printInfo];
    [printOperation setShowsPrintPanel:YES];
    [printOperation runOperationModalForWindow:[self window] delegate:nil didRunSelector:nil contextInfo:nil];
}

Not sure if this helps, but the main view does have setWantsLayers to YES, and here is one of my decorations :

CALayer *customerLayer = [self.customerView layer];
[customerLayer setCornerRadius:10];
[customerLayer setBackgroundColor:[NSColor colorWithDeviceWhite:0 alpha:0.30].CGColor];
[customerLayer setBorderColor:[NSColor blackColor].CGColor];
[customerLayer setBorderWidth:1.5];

When I display the window on screen, it looks just like I want it to, but the above round rect doesn't get printed, but all labels that are on top of it do.

Sometimes it is easier to just do custom drawing to reproduce simple effects provided by layers. But sometimes that is a real pain. To print layer backed views you can render them as an image first and print a NSImageView instead.

I give you a generic solution to convert an entire NSView with all its subviews and sublayers hierarchy into a NSImageView. Note that this is written with c# using MonoMac. You should be able to convert this easily to objC. Try searching for examples on Layer.RenderInContext for further references in objC. The method below worked for me on complex view hierarchies (ex: table views which contain semi transparent layer backed subviews with rounded corners in its row views):

//convert printView (your ready-to-print view which contains layer backed views) to a image view 
{
    //first we enclose the view in a new NSView - this fixes an issue which prevents the view to print upside down in some cases - for instance if printView is a NSScrollView, it will be drawn upside down
    NSView container = new NSView (printView.Frame);
    container.AddSubview (printView);
    printView = container;

    printView.WantsLayer = true;
    RectangleF bounds = printView.Bounds;
    int bitmapBytesPerRow = 4 * (int)bounds.Size.Width;
        using (CGColorSpace colorSpace = CGColorSpace.CreateDeviceRGB())
    using (CGBitmapContext context = new CGBitmapContext (null,
                                         (int)bounds.Width,
                                         (int)bounds.Height,
                                         8,
                                         bitmapBytesPerRow,
                                         colorSpace,
                                         CGImageAlphaInfo.PremultipliedLast)) {
        printView.Layer.RenderInContext (context);
        NSImageView canvas = new NSImageView (bounds);
        canvas.Image = new NSImage (context.ToImage (), bounds.Size); //instead of context.ToImage() in objC you have CGBitmapContextCreateImage
        printView = canvas;
    }
}

Layers don't appear on printing. I had to make my own NSView object and do a drawRect function to put as background views. Then it printed out automatically without doing anything.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM