Skip to main content


ยท 2 min read

In the macOS 10.13.4 beta, I spotted a new CALayer class called CAPortalLayer being used in AppKit, and apparently also in UIKit. After looking into it a bit more, it turns out it does exactly what CAPluginLayer did for CGSWindow/NSWindow but for CALayers!

I've neglected to provide a screenshot because it really doesn't look like much (it does update in realtime though).

let layer = CAPortalLayer()
layer.frame = CGRect(x: 10, y: 10, width: 200, height: 200)
layer.sourceLayer = self.view.layer!
//layer.sourceContextId = 0
//layer.sourceLayerRenderId = 0
layer.hidesSourceLayer = false // try out true as well!
//layer.matchesOpacity = true // apparently always true
//layer.matchesPosition = false // apparently always true
//layer.matchesTransform = true // apparently always true

Notice in the code sample, all we needed to do was set sourceLayer to an existing valid layer and it worked! There are two more interesting properties here as well: sourceLayerRenderId and sourceContextId -- it appears that you must set sourceLayerRenderId for sourceContextId to matter to the render server (backboardd on iOS and windowserver on macOS). What these properties allow you to do is "portal" a layer from any process whose context you know of - just like with CAPluginLayer. Just send the CAContext.contextId over the wire from a friendly process and it'll render. Unlike CALayerHost et al. however, the originating layer will also render on-screen (unless hidesSourceLayer is true). How do you get the sourceLayerRenderId you might ask? Here's the function prototype: NSUInteger CALayerGetRenderId(CALayer *); -- it won't be hard.

P.S. I originally tested this on iOS using the private _UIPortalView class. On macOS, AppKit has NSPortalView as well as NSPortalView1 and NSPortalView2 for some weird reason.