4

I have use [PDFView setNeedsDisplay:YES] to let the PDF view redraw, and it worked great on OSX 10.9-10.11. However it doesn't work unless I zoom in or zoom out the PDF page...

Is there any other way to redraw immediately? Code below:

NSRect      newBounds;
NSRect      currentBounds;
NSRect      dirtyRect;
NSPoint     mouseLoc;
NSPoint     endPt;

// Where is annotation now?
currentBounds = [_activeAnnotation bounds];

// Mouse in display view coordinates.
mouseLoc = [self convertPoint: [theEvent locationInWindow] fromView: NULL];

// Convert end point to page space.
if(activePage == nil)
    activePage =[_activeAnnotation page];

_LinePoint= [self convertPoint: mouseLoc toPage: activePage];
endPt = [self convertPoint: mouseLoc toPage: activePage];
if(_selectedIdx == 3) //ink
{
    [(PDFAnnotationInk*)_activeAnnotation removeBezierPath:_path];

    //endPt.x=_xPoint.x; //竖线
    //endPt.y=_xPoint.y; //横线

    [_path lineToPoint:endPt];  //  普通笔

    [(PDFAnnotationInk*)_activeAnnotation addBezierPath:_path];

    [self annotationChanged];
    [self setNeedsDisplay:YES];

    return;

UPDATE:

I found that the setNeedsDispaly calls the drawPage:toContext: however the drawing code doesn't work in drawPage:toContext:

- (void)drawPage:(PDFPage *)pdfPage toContext(CGContextRef)context
{
    [super drawPage: pdfPage toContext:context];
    NSBezierPath *line=[NSBezierPath bezierPath];
    [line moveToPoint:_xPoint];
    [line lineToPoint:NSMakePoint(150, 150)];
    [[NSColor redColor] set];
    [line setLineWidth:50] ;
    [line stroke];
}

the debug said CGContextSetFillColorWithColor: invalid context 0x0 and more invalid context 0x0 warnings. What I do in the drawPage:toContext: is testing and just use BezierPath to draw a line.

Parag Bafna
  • 22,812
  • 8
  • 71
  • 144
SuperBerry
  • 1,193
  • 1
  • 12
  • 28
  • 1
    Internally PDFView was significantly changed in 10.12 . It seems that the drawing mechanism is different now, it draws the pages asynchronously outside of the main thread. There seems to be no proper way to force PDFView to redraw itself or its pages. The drawing is only called in response to user interaction with the PDFView itself (scroll, resize). Shamefully, Apple didn't find it necessary to document any of these changes or provide developers with alternatives, not a word. I'm looking into ways to solve these issues with PDFView as well (and there are many) – danielv Oct 05 '16 at 10:55
  • Thanks for your reply... So we have to find other ways... Any suggestion? – SuperBerry Oct 05 '16 at 11:12
  • That depends on what exactly you are trying to achieve. Why do you need to redraw the PDFView/pages? – danielv Oct 05 '16 at 11:33
  • @danielv Annotations, like lines, text, image etc. – SuperBerry Oct 05 '16 at 12:06
  • Now I see the drawPage:toContext: was called after setNeedsDisplay: , but my drawing code doesn't work... ` - (void)drawPage:(PDFPage *)pdfPage toContext:(CGContextRef)context { [super drawPage: pdfPage toContext:context]; NSBezierPath *line=[NSBezierPath bezierPath]; [line moveToPoint:_xPoint]; [line lineToPoint:NSMakePoint(150, 150)]; [[NSColor redColor] set]; [line setLineWidth:50] ; [line stroke]; NSLog(@"drawPagetoContext"); }`the debug said "CGContextSetFillColorWithColor: invalid context 0x0" and more "invalid context 0x0" warnings. – SuperBerry Oct 06 '16 at 08:23
  • PDFView's `setNeedsDisplay` does nothing for drawing pages in 10.12. PDFView will call `drawPage:toContext:` when it decides that pages needs to be rendered, then it caches them until they need to be re-rendered. So far, I haven't found any reasonable and reliable way to force PDFView to render its pages programmatically. – danielv Oct 06 '16 at 08:28
  • 1
    Also, Cocoa drawing methods won't work in PDFView's `drawPage:toContext:` or PDFPage's `drawWithBox:toContext:` because they are called on a different thread which doesn't have NSGraphicsContext set. You need to use the passed context explicitly. – danielv Oct 06 '16 at 08:31
  • @danielv can I draw on the correct thread? Or draw by PDFPage class? – SuperBerry Oct 06 '16 at 09:06
  • If you want to draw on the PDF page displaying **on screen** inside PDFView, you need to override PDFPage's `drawWithBox:toContext:` and do the drawing there. There you can use the `CGContext` passed to you with the Quartz API's or you can try to create an `NSGraphicsContext` from it and set it as the current context, then you can use the Cocoa drawing API (I didn't fully test this). Also, you will need to subclass PDFDocument and override its `pageClass` to return the class of your PDFPage's sublclass. – danielv Oct 06 '16 at 09:22
  • @danielv I have done what you suggested, but MacOS sierra is so wierd... Now the page will show some blank blocks... It seems the redrawing is still broken.. – SuperBerry Oct 06 '16 at 15:25
  • Any update? Does it still not work? – Nisba Oct 24 '16 at 15:00
  • Yes, still doesn't work. I don't know what the apple is thinking. – SuperBerry Oct 24 '16 at 23:17

1 Answers1

0

I'm having the same trouble. The first time I add an annotation, PDFView displays that annotation on the page immediately. From then on, adding or removing an annotation works fine in code but PDFView doesn't show the change until I manually scroll the view.

From PDFKit I've tried:

previewView.layoutDocumentView()

for pageIndex in 0...pdf.pageCount - 1 {
  let page = pdf.page(at: pageIndex)!
  previewView.annotationsChanged(on: page)
}

and from NSView I've tried:

previewView.needsDisplay = true
previewView.needsLayout = true
previewView.documentView?.needsDisplay = true
previewView.updateLayer()

but no luck. I've tried scrolling the PDFView with code too but it hasn't been a reliable way of sneaking a refresh, and in general shouldn't be the way to do this.

sahandnayebaziz
  • 622
  • 7
  • 20
  • Yes.... It is not the good way. I use zoom in and 0.3 seconds later I zoom out... – SuperBerry Oct 06 '16 at 13:46
  • But strange, I added the annotation, the PDFView doesn't show unless I scroll or zoom. Is it possible to show your code about the adding annotation? – SuperBerry Oct 06 '16 at 13:47
  • @SuperBerry I add the annotation like this to a PDFView that is already on the screen: `pdf.page(at: i)?.addAnnotation(annotation)`. The annotation is a text annotation. After this line, the annotation immediately shows. After that shows, the PDFView doesn't update any additions or removals again until I manually scroll – sahandnayebaziz Oct 07 '16 at 11:13
  • Do you mean after addAnnotation: method, you can see the text immediately, or you need to scroll? – SuperBerry Oct 07 '16 at 14:24
  • after calling `addAnnotation`, I can see the annotation immediately. Every other time I call `addAnnotation` or `removeAnnotation`, I need to scroll to see them. @SuperBerry – sahandnayebaziz Oct 07 '16 at 15:04
  • Also might be worth mentioning that I am adding the same annotation to every page of the PDF. So I have a for loop going through each page and numbering the pages, adding one annotation to each page. That might be why I see it the first time. Updating the annotations (again, on all pages at once in a for loop) does not show immediately, I have to scroll the view, and none of the methods in my original response work – sahandnayebaziz Oct 07 '16 at 15:05
  • Yes, that is the reason. If you add to only one page, you would not see it... That confuse me.. – SuperBerry Oct 07 '16 at 23:13
  • Are there any open radars for this? – sahandnayebaziz Oct 07 '16 at 23:13