简体   繁体   中英

iOS: Rendering UITableView into PDF

I have written pdfGenerator class to render whole UITableView to generate PDF.

Usage:

[pdfGenerator createPDFFromTableview:self.tableView];

Class Methods:

- (void)createPDFFromTableview:(UITableView *)tableview {
    [tableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
    [self renderTableView:tableview];

    int rows = [tableview numberOfRowsInSection:0];
    int numberofRowsInView = 17;
    for (int i = 0; i < rows/numberofRowsInView; i++) {
        [tableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:(i+1)*numberofRowsInView inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
        [self renderTableView:tableview];
    }
}

- (void)renderTableView:(UITableView*)tableview {
    UIGraphicsBeginImageContext(tableview.frame.size);
    [tableview.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *tableviewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    UIGraphicsBeginPDFPage();
    [tableviewImage drawInRect:tableview.frame];
}

This code scrolls tableview correctly but it create PDF only for first page and rest of pages are coming blank! I have no idea why it is happening while it should take current uitableview snapshot as is and render but it seems to do this only for first page. Can someone point out what could be wrong here? Is there any better way doing what I am trying to achieve?

[EDIT]

Following gives the same result;

int totalRows = [tableview numberOfRowsInSection:0];
int totalVisibleRows = 17;
for (int i = 0; i < ceil((float)totalRows/totalVisibleRows); i++) {
    [tableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:i * totalVisibleRows inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];

    [pdfGenerator renderTableView:tableview indexPathStart:[NSIndexPath indexPathForRow:i * totalVisibleRows inSection:0] indexPathEnd:[[tableview indexPathsForVisibleRows] lastObject]];
}

- (void)renderTableView:(UITableView*)tableview indexPathStart:(NSIndexPath *)startingIndexPath indexPathEnd:(NSIndexPath *)endIndexPath {
    CGRect rectToDraw = CGRectUnion([tableview rectForRowAtIndexPath:startingIndexPath], [tableview rectForRowAtIndexPath:endIndexPath]);

    NSLog(@"%@", NSStringFromCGRect(rectToDraw));

    UIGraphicsBeginImageContext(rectToDraw.size);
    [tableview.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *tableviewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    UIGraphicsBeginPDFPage();
    [tableviewImage drawInRect:rectToDraw];
}

Output rectToDraw:

{{0, 0}, {768, 720}}
{{0, 680}, {768, 720}}
{{0, 1360}, {768, 720}}
{{0, 2040}, {768, 360}}

I end-up rendering the view which is holding tableview after scrolling tableview programmatically. Rendering tableview doesn't seem to be working after scroll!

Your graphics context is initialized with the frame of the UITableView , so it'll always look at the 'first page'. The graphics context for the following 'pages' would need the context offset of the internal UIScrollView of your UITableView . Barring any trickery to access the UIScrollView directly, I suggest you use the [UITableView rectForRowAtIndexPath:] method to get the CGRects of the first and last row you want to draw in the step (ie page) and CGRectUnion them to get a rect which will include both (and by that all the rows inbetween).

Edit (as requested in comments): You'd basically replace the rendering message in your loop with something like that

[self renderTableView:tableview
       indexPathStart:[NSIndexPath indexPathForRow:row inSection:0]
         indexPathEnd:[[tableview indexPathsForVisibleRows] lastObject]];

And your rendering method would then look like:

- (void)renderTableView:(UITableView*)tableview indexPathStart:(NSIndexPath *)startingIndexPath indexPathEnd:(NSIndexPath *)endIndexPath{
    CGRect rectToDraw = CGRectUnion([tableview rectForRowAtIndexPath:startingIndexPath], [tableview rectForRowAtIndexPath:endIndexPath]);
    NSLog(@"%@", NSStringFromCGRect(rectToDraw));
   //...
}

As to your question about the indexPaths. The indexPath.row will not be always 0-16. They will be 0-16, 17-34, 35-51, etc. This also reflects on your context, ie you want to draw the rect for the relevant indexPath rectangle, as this is the 'place' where the layer backing the UITableView has the backing store for the currently rendered graphics. Not knowing your PDF drawing routine, you might need to check your drawing rect there too.

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