Skip to content

UI Events

In this example, we will show you how to configure the callbacks of various editor events for the mobile editor. The example is based on the Design Editor, however, it is exactly the same for all the other solutions.

Note that the bodies of all callbacks except onUpload are copied from the Design Editor default implementations.

Import

In addition to importing an editor module, you also need to import the engine module if you are explicitly referencing its symbols like Engine or MIMEType in the following.

import IMGLYDesignEditor
import IMGLYEngine

Modifiers

After initializing an editor SwiftUI view you can apply any SwiftUI modifier to customize it like for any other SwiftUI view. All public Swift extensions of existing types provided by IMG.LY, e.g., for the SwiftUI View protocol, are exposed in a separate .imgly property namespace. The callbacks to customize the editor behavior are no exception to this rule and are implemented as SwiftUI modifiers.

The default implementation of the callbacks depends on the used editor solution as each editor provides the most reasonable default behavior for its use case with minimal required code. In addition to controlling the engine, some of the callbacks receive the EditorEventHandler parameter that can be used to send UI events.

DesignEditor(settings)
  • onCreate - the callback that is invoked when the editor is created. This is the main initialization block of both the editor and engine. Normally, you should load or create a scene as well as prepare asset sources in this block. This callback does not have a default implementation, as default scenes are solution-specific, however, OnCreate.loadScene contains the default logic for most solutions. By default, it loads a scene and adds all default and demo asset sources.
.imgly.onCreate { engine in
// Load or create scene
try await engine.scene.load(from: DesignEditor.defaultScene) // or `engine.scene.create*`
// Add asset sources
try await engine.addDefaultAssetSources(baseURL: Engine.assetBaseURL)
try await engine.addDemoAssetSources(sceneMode: engine.scene.getMode(),
withUploadAssetSources: true)
try await engine.asset.addSource(TextAssetSource(engine: engine))
}
  • onExport - the callback that is invoked when the export button is tapped. You may want to call one of the export functions in this callback. The default implementations call BlockAPI.export or BlockAPI.exportVideo based on the engine’s SceneMode, display a progress indicator for video exports, write the content into a temporary file, and open a system dialog for sharing the exported file.
.imgly.onExport { mainEngine, eventHandler in
// Export design scene
@MainActor func export() async throws -> (Data, MIMEType) {
guard let scene = try mainEngine.scene.get() else {
throw CallbackError.noScene
}
let mimeType: MIMEType = .pdf
let data = try await mainEngine.block.export(scene, mimeType: mimeType) { backgroundEngine in
// Modify state of the background engine for export without affecting
// the main engine that renders the preview on the canvas
try backgroundEngine.scene.getPages().forEach {
try backgroundEngine.block.setScopeEnabled($0, key: "layer/visibility", enabled: true)
try backgroundEngine.block.setVisible($0, visible: true)
}
}
return (data, mimeType)
}
// Export video scene
@MainActor func exportVideo() async throws -> (Data, MIMEType) {
guard let page = try mainEngine.scene.getCurrentPage() else {
throw CallbackError.noPage
}
eventHandler.send(.exportProgress(.relative(.zero)))
let mimeType: MIMEType = .mp4
let stream = try await mainEngine.block.exportVideo(page, mimeType: mimeType) { backgroundEngine in
// Modify state of the background engine for export without affecting
// the main engine that renders the preview on the canvas
}
for try await export in stream {
try Task.checkCancellation()
switch export {
case let .progress(_, encodedFrames, totalFrames):
let percentage = Float(encodedFrames) / Float(totalFrames)
eventHandler.send(.exportProgress(.relative(percentage)))
case let .finished(video: videoData):
return (videoData, mimeType)
}
}
try Task.checkCancellation()
throw CallbackError.couldNotExport
}
// Export scene based on `SceneMode`
let data: Data, mimeType: MIMEType
switch try mainEngine.scene.getMode() {
case .design: (data, mimeType) = try await export()
case .video: (data, mimeType) = try await exportVideo()
@unknown default:
throw CallbackError.unknownSceneMode
}
// Write and share file
let url = FileManager.default.temporaryDirectory.appendingPathComponent(
"Export",
conformingTo: mimeType.uniformType
)
try data.write(to: url, options: [.atomic])
switch try mainEngine.scene.getMode() {
case .design: eventHandler.send(.shareFile(url))
case .video: eventHandler.send(.exportCompleted { eventHandler.send(.shareFile(url)) })
@unknown default:
throw CallbackError.unknownSceneMode
}
}
  • onUpload - the callback that is invoked after an asset is added to an asset source. When selecting an asset to upload, a default AssetDefinition object is constructed based on the selected asset and the callback is invoked. By default, the callback leaves the asset definition unmodified and returns the same object. However, you may want to upload the selected asset to your server before adding it to the scene. This example demonstrates how you can access the URL of the new asset, use it to upload the file to your server, and then replace the URL with the URL of your server.
.imgly.onUpload { engine, sourceID, asset in
var newMeta = asset.meta ?? [:]
for (key, value) in newMeta {
switch key {
case "uri", "thumbUri":
if let sourceURL = URL(string: value) {
let uploadedURL = sourceURL // Upload the asset here and return remote URL
newMeta[key] = uploadedURL.absoluteString
}
default:
break
}
}
return .init(id: asset.id, groups: asset.groups, meta: newMeta, label: asset.label, tags: asset.tags)
}