IconJar is a very simple native macOS application, but behind the scenes there are a lot of things going on, from a 3.5k line custom Grid View to allow smooth 60 frames per second scrolling, interactions and animations, to a completely new open source SVG rendering library solely created to allow any developer creating a macOS application to with just one line of code, render an SVG into any graphics context.
IconJar’s Grid View is not a standard NSCollectionView (Apples built in way of laying out a grid of items), in-fact, its not an NSCollectionView at all, its completely custom to fit our needs. We wanted our Grid View to be able to maintain 60 frames per second when scrolling, draw any number of items efficiently and consume little memory. We also needed to be able to animate the cells beautifully, but unfortunately AppKit’s Collection View was unable to provide us with such functionality. At heart, our Grid View (IJGridView) is a track laying system (much like how UITableView’s and NSTableView’s work) in that we only render and manipulate cells that are directly on the screen. This also works the same way with the allocation side of things, we keep reference to off screen cells in a queue and re-use them when we can to stop expensive memory allocations slowing down the performance of scrolling.
For the more interested, you can view the header file for our Grid View here.
IJGridView is a CALayer based view, “CA” is Apples Core Animation library, for great performance of rendering and animations, which are mostly performed on the GPU. All cells are drawn directly to the current graphics context or into a CALayer for placement and animation. All images within are Asynchronously loaded in a background threads using GCD (Grand Central Dispatch), which leads us on to drawing icons off screen (preloading them in the background without needing them to be drawn directly to screen at the point of loading them). IconJar relies on creating previews of any icon off-screen, this is great for performance but a nightmare in code terms as we support SVG’s, any SVG library out there was either to limiting in terms of features, or, in our case, did not support being allocated on a background thread. Which leads us on to the main core and library of IconJar, IJSVG.
IJSVG is IconJar’s SVG rendering library. We wrote it to tackle the main gripes and flaws the other libraries have. It supports being rendered and being called on background threads and is built upon Apples CALayer library.
Parsing SVG’s is not easy, in-fact, the whole SVG spec is not easy to follow, its difficult to work out what we want to attempt to parse, as most people use SVG’s within a browser which happily support modern CSS and can deal with malformed documents and character encodings. That being said, IJSVG’s rendering stack looks a little something like this:
At this point, we have a node tree in memory with everything needed to actually render it. But of course, we actually need a renderer. IJSVG’s renderer is simple, it takes the tree and creates a CALayer from any given node. You can give it any node at any depth in the tree, this allows us to create a layer from only certain parts of the SVG if we wanted. At this point, to save on memory, we can allow the developer who is using our library to discard the node tree and only use the built tree (it will lazily create it again if needed). Now we have a CALayer of what the SVG should actually look like, we can composite that into an NSImage or CGImage very easily with only a few lines of code.
IJSVG also allows exporting, this is actually one of the most complicated parts, it allows you to export to an SVG file (or string) from just the CALayer tree (thats insane!). This also allows us to create SVG’s at runtime from a CALayer (but must be a IJSVGLayer subclassed layer) and export those (this is how we convert bitmap’s to being embedded inside SVG documents). We use this to colorise and resize icons when needed, if we want to colorise an icon, we have properties on the main SVG object (fillColor, strokeColor) which when being rendered will overwrite any possible fill value of the node (this includes gradients and patterns etc).
IJSVG has been open sourced and can be viewed here.
All icons being rendered in IconJar are routed and stored through our internal caching system. This system is also asynchronous and handles the creation of images at any given size, for instance, if we ask it for the same image twice for the exact same size, why generate it twice? The caching system we allow to use about 20MB of memory before it starts to discard its contents, which we felt was a happy medium of memory and performance.
IconJar is a very simple application to use but behind the scenes there are a lot of things going on, from a 3.5k line custom Grid View to allow smooth 60 frames per second scrolling, interactions and animations, to a completely new SVG rendering library solely created to allow any developer creating a macOS application to with only one line of code, render an SVG into any graphics context.
We hope this gives you a little taste to what is going on behind the scenes when you launch IconJar.