Enforce specific aspect ratios or fixed dimensions on design blocks using the force crop API.

Overview#
Force cropping ensures that a page or any croppable block uses a specific aspect ratio or fixed size when the editor loads. We send an .applyForceCrop event through the editor’s event handler, providing the target design block, an array of ForceCropPreset candidates, and a ForceCropMode.
| Type | Purpose |
|---|---|
ForceCropPreset | Identifies a crop preset by source ID and preset ID |
ForceCropMode | Controls crop UI behavior: .silent, .always, or .ifNeeded |
When multiple candidates are provided, the system automatically selects the best match based on the block’s current dimensions.
Setting Up Force Crop#
In the onLoaded callback, we get the current page and send the force crop event with preset candidates from the default ly.img.crop.presets asset source, which is registered by addDefaultAssetSources.
builder.onLoaded { context, existing in guard let page = try context.engine.scene.getCurrentPage() else { return }
let sourceID = Engine.DefaultAssetSource.cropPresets.rawValue
context.eventHandler.send(.applyForceCrop( to: page, with: [ ForceCropPreset(sourceID: sourceID, presetID: "aspect-ratio-1-1"), ForceCropPreset(sourceID: sourceID, presetID: "aspect-ratio-16-9"), ForceCropPreset(sourceID: sourceID, presetID: "aspect-ratio-9-16"), ], mode: .ifNeeded, ))
try await existing()}Applying Force Crop#
We send the .applyForceCrop event with the page, preset candidates, and mode. The event can be fired outside onLoaded as well — at any appropriate time in your business logic, but not in onCreate.
context.eventHandler.send(.applyForceCrop( to: page, with: [ ForceCropPreset(sourceID: sourceID, presetID: "aspect-ratio-1-1"), ForceCropPreset(sourceID: sourceID, presetID: "aspect-ratio-16-9"), ForceCropPreset(sourceID: sourceID, presetID: "aspect-ratio-9-16"), ], mode: .ifNeeded,))Understanding Crop Modes#
The ForceCropMode parameter controls how the editor responds after applying a crop preset.
| Mode | Behavior |
|---|---|
.silent | Applies the crop without opening the crop UI. |
.always | Applies the crop and opens the crop sheet immediately. |
.ifNeeded | Compares the preset with the current frame dimensions. The crop is applied and the sheet opens only if the ratio/size differs materially. |
Isolating Presets#
To ensure the user only sees the enforced preset, we recreate the asset source in the onLoaded callback:
let sourceID = Engine.DefaultAssetSource.cropPresets.rawValuetry context.engine.asset.removeSource(sourceID: sourceID)try context.engine.asset.addLocalSource(sourceID: sourceID)try context.engine.asset.addAsset(to: sourceID, asset: myCustomPreset)Next Steps#
- Hide Elements - Remove or hide UI components
- Rearrange Buttons - Change button order across components