Search Docs
Loading...
Skip to content

Set Zoom Level

Control how much of a design is visible by driving the camera zoom from code. Set an exact zoom level, frame a block, follow content as it resizes, constrain the camera, keep content clear of overlays, and react to zoom changes — all through the Engine API.

7 mins
estimated time
GitHub

The zoom level is a ratio between design pixels and screen pixels. A zoom level of 1.0 shows one design pixel as one screen pixel; 2.0 shows it as two. Every call below operates on the engine’s active scene and reads back through engine.scene.

Get and Set the Zoom Level#

Set an absolute zoom level with setZoom(_:) and read the current one with getZoom(). Reading the current value first lets you apply a relative change, such as halving the zoom.

try engine.scene.setZoom(1.0)
let currentZoom = try engine.scene.getZoom()
try engine.scene.setZoom(0.5 * currentZoom)

Zoom to a Block#

Frame a specific block — the page, a group, or any element — with zoom(to:paddingLeft:paddingTop:paddingRight:paddingBottom:). Without padding the camera fits the block tightly; padding (in points) leaves breathing room on each side.

immediateZoom(to:…forceUpdate:) performs the same framing synchronously. It assumes the layout is already up to date; pass forceUpdate: true to run a layout pass first.

try await engine.scene.zoom(
to: page,
paddingLeft: 20,
paddingTop: 20,
paddingRight: 20,
paddingBottom: 20,
)
try engine.scene.immediateZoom(
to: page,
paddingLeft: 20,
paddingTop: 20,
paddingRight: 20,
paddingBottom: 20,
forceUpdate: true,
)

Auto-Fit Zoom#

Auto-fit continuously refits a block as its bounding box changes. Choose the axis to follow with ZoomAutoFitAxis — .both, .horizontal, or .vertical. Auto-fit only takes effect while the zoom level is not being driven by an editor UI layer, and calling setZoom(_:) or zoom(to:) disables it.

try engine.scene.enableZoomAutoFit(
page,
axis: .both,
paddingLeft: 20,
paddingTop: 20,
paddingRight: 20,
paddingBottom: 20,
)

Disable Auto-Fit#

Stop following a block with disableZoomAutoFit(_:), and check whether auto-fit is currently enabled with isZoomAutoFitEnabled(_:).

let autoFitEnabled = try engine.scene.isZoomAutoFitEnabled(page)
print("Auto-fit enabled: \(autoFitEnabled)")
try engine.scene.disableZoomAutoFit(page)

Limit the Zoom Range#

unstable_enableCameraZoomClamping(_:minZoomLimit:maxZoomLimit:…) keeps the camera zoom within a range relative to the given blocks. A negative limit means unbounded. Query whether clamping is active with unstable_isCameraZoomClampingEnabled(_:) and remove it with unstable_disableCameraZoomClamping(). These camera-clamping APIs are experimental, indicated by the unstable_ prefix.

try engine.scene.unstable_enableCameraZoomClamping(
[page],
minZoomLimit: 0.125,
maxZoomLimit: 8.0,
)
let zoomClampingEnabled = try engine.scene.unstable_isCameraZoomClampingEnabled(scene)
print("Zoom clamping enabled: \(zoomClampingEnabled)")
try engine.scene.unstable_disableCameraZoomClamping()

Constrain the Camera Position#

unstable_enableCameraPositionClamping(_:…) keeps the camera within the bounds of the given blocks, so panning never leaves the content. Padding (in points) defines how far past the bounds the camera may move. Query with unstable_isCameraPositionClampingEnabled(_:) and remove with unstable_disableCameraPositionClamping(). Camera clamping also integrates with the editor’s global settings — see the Settings guide.

try engine.scene.unstable_enableCameraPositionClamping(
[scene],
paddingLeft: 10,
paddingTop: 10,
paddingRight: 10,
paddingBottom: 10,
)
let positionClampingEnabled = try engine.scene.unstable_isCameraPositionClampingEnabled(scene)
print("Position clamping enabled: \(positionClampingEnabled)")
try engine.scene.unstable_disableCameraPositionClamping()

Keep Content Clear of Overlays#

When your own UI covers an edge of the canvas, such as a toolbar or a side panel, set safe area insets with engine.editor.setSafeAreaInsets(left:top:right:bottom:). Zoom, panning and clamping keep content out of the insets and center it in the remaining area. Each inset is in points, and a side you leave out keeps its current value. Read the current values with getSafeAreaInsets(), and set all sides to 0 to turn them off.

Unlike the padding of zoom(to:…), the insets apply to every camera operation, so content stays clear of the overlay when the user pans or pinches. On iOS, the prebuilt editors set the left and right insets themselves on every canvas resize, so use this API when you build your own UI on the Engine.

try engine.editor.setSafeAreaInsets(right: 80)
let safeAreaInsets = engine.editor.getSafeAreaInsets()
print("Safe area insets: \(safeAreaInsets)")
try engine.editor.setSafeAreaInsets(left: 0, top: 0, right: 0, bottom: 0)

Subscribe to Zoom Changes#

onZoomLevelChanged is an AsyncStream that emits whenever the zoom level changes. Iterate it in a Task to keep custom UI — a zoom indicator, say — in sync, and cancel the task when you no longer need updates.

let zoomTask = Task {
for await _ in engine.scene.onZoomLevelChanged {
let zoom = try engine.scene.getZoom()
print("Zoom level changed: \(zoom)")
}
}
try engine.scene.setZoom(2.0)
zoomTask.cancel()

API Reference#

Methods#

Method Description
setZoom(_:) Set the active scene’s zoom level, in unit 1/px.
getZoom() Read the active scene’s current zoom level.
zoom(to:paddingLeft:paddingTop:paddingRight:paddingBottom:) Animate the camera to frame a block, with optional per-side padding in points.
immediateZoom(to:paddingLeft:paddingTop:paddingRight:paddingBottom:forceUpdate:) Frame a block without animation; pass forceUpdate: true to run a layout pass first.
enableZoomAutoFit(_:axis:paddingLeft:paddingTop:paddingRight:paddingBottom:) Continuously refit a block on the given ZoomAutoFitAxis.
disableZoomAutoFit(_:) Stop a previously enabled auto-fit.
isZoomAutoFitEnabled(_:) Query whether auto-fit is enabled.
unstable_enableCameraZoomClamping(_:minZoomLimit:maxZoomLimit:…) Constrain the zoom range relative to the given blocks. Experimental.
unstable_disableCameraZoomClamping() Remove zoom clamping. Experimental.
unstable_isCameraZoomClampingEnabled(_:) Query whether zoom clamping is enabled. Experimental.
unstable_enableCameraPositionClamping(_:…) Keep the camera within the bounds of the given blocks. Experimental.
unstable_disableCameraPositionClamping() Remove position clamping. Experimental.
unstable_isCameraPositionClampingEnabled(_:) Query whether position clamping is enabled. Experimental.
engine.editor.setSafeAreaInsets(left:top:right:bottom:) Keep content clear of UI that covers the canvas edges, in points. A side you leave out keeps its value.
engine.editor.getSafeAreaInsets() Read the current safe area insets as SafeAreaInsets.

Properties#

Property Type Description
onZoomLevelChanged AsyncStream<Void> Emits whenever the zoom level changes.

Troubleshooting#

Problem Resolution
Zoom level doesn’t change Confirm a scene exists before calling zoom methods, and that no editor UI layer is overriding the zoom.
Auto-fit has no effect Only one block per scene can drive auto-fit, and setZoom(_:) or zoom(to:) disables it. Pass a valid block that belongs to the active scene.
Zoom feels capped An active zoom clamp limits the range. Check with unstable_isCameraZoomClampingEnabled(_:) and adjust or remove it.
Content sits off center Safe area insets move the center of every zoom. Check them with engine.editor.getSafeAreaInsets().
setSafeAreaInsets throws Each inset must be a finite number of 0 or more.

Next Steps#