-[NSView (un)lockFocus] deprecated, what to use instead?

I am hitting major road blocks in migrating one of my Obj-C-Cocoa applications away from -[NSView (un)lockFocus] and -[NSBitmapImageRep initWithFocusedViewRect:].

In a transcript of a presentation on WWDC2018 I read:

With our changes to layer backing, there's a few patterns I want to call out that aren't going to work in macOS 10.14 anymore. If you're using NSView lockFocus and unlockFocus, or trying to access the window's graphics contents directly, there's a better way of doing that. You should just subclass NSView and implement draw rect. ...

Of course, we all implemented -[NSView drawRect:] for decades now. The big question is, how can we do incremental (additional, event driven) drawing in our views, without redrawing the whole view hierarchy. This is the use case of -(un)lockFocus, and especially when drawing of the base view is computational expensive. Wo would have thought that people use -(un)lockFocus for regular drawing of the NSView hierarchy.

I tried to get away with CALayer, only to find out after two days experimenting with it, that a sublayer can only be drawn if the (expensive) main layer has been drawn before —> dead end road.

Now I am going to implement a context dependent -[NSView drawRect:]. Based on a respective instance variable, either of the (expensive) base presentation of the view or the simple additions are drawn. Is it that what Apple meant by … just subclass NSView and implement draw rect?

From the point of view of object oriented programming, using switch() in methods to change the behaviour of the object is ugly - to say the least. Any better options?

Ugly or not, in any case, I don’t want to redraw the whole view hierarchy only for moving a crosshairs in a diagram.

My actual use case is:

This application draws into a custom diagram NSView electrochemical measurement curves which may consist of a few thousands up to millions of data points. The diagram view provides a facility for moving crosshairs and other pointing aids over the displayed curves, by dragging/rolling with the mouse or the touch pad, or by moving it point by point with the cursor keys.

Diagram generation is computational expensive and it must not occur only because the crosshairs should be moved to the next data point.

So for navigating the crosshairs (and other pointing aids), a respective method locks the focus of said view, restores the background from a cache, caches the background below the new position of the crosshairs using -[NSBitmapImageRep initWithFocusedViewRect:], draws the crosshairs and finally unlocks the focus.

All this does not work anymore since 10.14.

I'm sad that there is not a reply to this after a whole year. It seems this is an obvious use case and I am running into a similar situation.

You can move your crosshairs to a separate CALayer or to multiple one, so you don't have to redraw anything but just changed the coordinate of such layers. No need to redraw the diagram each time. Or use CATiledLayer or something similar.

You can still make a NSBitmapImageRep manually and then get a NSGraphicsContext from it to draw into.

The following code converts a PDF NSImage ([self image]) into a bitmap alternate image for drawing faster. Saving the graphics state, use setCurrentContext is roughly equivalent to lockFocus. Then draw. Then restoreGraphicsState which serves as unlockFocus

I imagine you can adapt it to suit your needs.

        NSSize imgsize = [[self image] size];
        
        NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc]
                                      initWithBitmapDataPlanes:NULL
                                      pixelsWide:imgsize.width
                                      pixelsHigh:imgsize.height
                                      bitsPerSample:8
                                      samplesPerPixel:4
                                      hasAlpha:YES
                                      isPlanar:NO
                                      colorSpaceName:NSDeviceRGBColorSpace
                                      bytesPerRow:4 * imgsize.width
                                      bitsPerPixel:0
                                      ];
        NSImage* aimage = [[NSImage alloc] initWithSize:imgsize];
        [aimage addRepresentation:imageRep];
        [imageRep autorelease];
        // Draw into graphics context instead of using lockFocus
        NSGraphicsContext *g = [NSGraphicsContext graphicsContextWithBitmapImageRep:imageRep];
        [NSGraphicsContext saveGraphicsState];
        [NSGraphicsContext setCurrentContext:g];
        //[aimage lockFocus]; //<- deprecated
        NSRect adr = NSMakeRect(0, 0, imgsize.width, imgsize.height);
        // ----- draw content here -----
        //if ( [[self image] respondsToSelector:@selector(drawInRect:)] ) {   //10.9+, so no longer needs alternate...
            [[self image] drawInRect:adr];
        //} else {
        //    [[self image] drawInRect:adr fromRect:adr operation: NSCompositingOperationSourceOver fraction:1.0];
        //}
        //[aimage unlockFocus]; //<- deprecated
        [NSGraphicsContext restoreGraphicsState];
        [aimage autorelease];
        /*
        // ---------------- new method without lockFocus/unlockFocus ----------------
        NSImage* aimage = [NSImage imageWithSize:imgsize flipped:NO drawingHandler:^BOOL(NSRect dstRect) {
            //draw here
            [[self image] drawInRect:dstRect];
            NSLog(@"_draw code run");
            return YES;
        }];
         // IMPORTANT:  this does not do what we want and create a bitmap for faster drawing
         // instead it replays vector paths which is NOT what we want, the image is as slow as the original PDF
         // if content is bitmaps, then imageWithSize:flipped:drawingHandler: is fine
        */
        [self setAltImage:aimage];
-[NSView (un)lockFocus] deprecated, what to use instead?
 
 
Q