Skip to content

Instantly share code, notes, and snippets.

@nil-ableton
Last active November 1, 2017 11:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nil-ableton/84187254cd0ae1d5aa212ecf6b6e50c0 to your computer and use it in GitHub Desktop.
Save nil-ableton/84187254cd0ae1d5aa212ecf6b6e50c0 to your computer and use it in GitHub Desktop.
Core Animation

@url: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreAnimation_guide/Introduction/Introduction.html

Notes about using Core Animation for Macos applications.

Introducing Core Animation

For graphics, Apple describes Core Animation as the level just below AppKit. Therefore anything we would like to achieve with AppKit has to be provided via Core Animation already.

Core Animation's main purpose is to composite layers of bitmap content onto the display. Layers are arranged in trees and their properties are animated by Core Animation.

A layer is made of properties:

  • bounds rectangle
  • position
  • opacity
  • transform
  • contentsScale, the scale for retina displays

Core Animation's default is to animate these properties as they are changed. An animation is said to represent an Action.

Dependencies

QuartzCore framework

Integration into NSView

A NSView asks for Core Animation to be activated by saying it wants a layer:

setWantsLayer: { return YES; }

NSView can either be:

  • layer-backed, where layer creation and updates are automatic
  • layer-hosting, where it's the application that takes those responsibilities.

A layer-backed view gets a default CALayer layer, which can be changed by overriding makeBackingLayer.

Why would one create a layer-hosting view? When you want to chose your own layer class and you would like to configure a hierachy of layers associated with a single view.

To create set a NSView as layer-hosting, one simply calls setLayer on it. From that point on the view has to update its layer's properties correctly when something change.

Some types of layers:

  • CALayer: default layer type
  • CAMetalLayer: when you want to draw using the Metal rendering pipeline
  • CAOpenGLLayer: when you want to emit OpenGL commands

@note: is CALayer accelerated already? In which case for a bitmap based app, CALayer is the minimal API to use.

By default the NSView gets associated as a delegate of the layer, and its drawRect procedure will be called to draw into a bitmap. This is the recommended method for layer-backed views with dynamic rendering. For retina support, the layer's image will automatically be created at the right size.

For performing native OpenGL or Metal rendering, since Macos 10.8 one can ask the updateLayer procedure to get called by overriding:

wantsUpdateLayer: { return YES; }
updateLayer // this is where the rendering now takes place

So when is redrawing taking place? This is controlled by what layerContentsRedrawPolicy returns.

During resize, the default policy (NSViewLayerContentsRedrawDuringViewResize) is to call the drawRect procedure continuously. Alternatively, one could set the recommened NSViewLayerContentsRedrawOnSetNeedsDisplay which requires the view to decide when to redraw itself, in absence of that it will automatically stretch the content as it gets resized. This would be the best method if the UI was entirely constructed of composited CALayers, because the Core Animation system would then be able to redraw a close-enough approximation of the desired view by moving the layers with respect to one another.

The CALayer uses a CAMediaTiming & CADisplayLink to synchronize rendering with physical display frames, and provide time information to the drawing procedure, such as the presentation time of the previous frame and estimate the approximate presentation time for the next frame.

CADisplayLink is the class that synchronizes display and rendering:

@url: https://developer.apple.com/documentation/quartzcore/cadisplaylink

if your application cannot provide frames in the time provided, you may want to choose a slower frame rate. An application with a slower but consistent frame rate appears smoother to the user than an application that skips frames.

Actual frame rates are always a factor of the maximum refresh rate of the device.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment