Search
Loading...
Skip to content

Navigation Bar

The navigation bar serves as the primary control interface at the top of the editor, housing essential functions like session management (close/save), editing operations (undo/redo), mode switching, and export capabilities. This guide shows you how to customize the navigation layout, button placement, and functionality to align with your app’s information architecture and user flow patterns. While examples use the Design Editor, the same configuration principles apply to all editor solutions.

Explore a complete code sample on GitHub.

Navigation Bar on iOS

The navigation bar displays horizontally at the top of the editor, organized into three placement areas: leading (left), principal (center), and trailing (right).

Key Components:

  • NavigationBar.Item - Protocol that all navigation bar items conform to
  • NavigationBar.Button - Pre-built button implementation with action and label
  • NavigationBar.ItemGroup - Container that groups items by placement (leading, principal, trailing)
  • NavigationBar.Context - Provides access to the engine, editor state, and event handler
  • Custom Items - Create fully custom components by implementing NavigationBar.Item

Configuration#

Navigation bar customization uses SwiftUI modifiers in the .imgly namespace. Items are organized into placement groups similar to SwiftUI’s ToolbarItemGroup.

Available modifiers:

  • navigationBarItems - Define the complete list of navigation bar items grouped by placement. Items are only displayed when isVisible(_:) returns true.

  • modifyNavigationBarItems - Modify the default item list by adding, replacing, or removing specific items without rebuilding the entire configuration.

The NavigationBar.Context provides access to the engine, editor state, asset library, and event handler for advanced customization logic.

Default Navigation Bar Items#

Each editor solution has its own default navigation bar configuration optimized for its workflow:

Design Editor:

.imgly.navigationBarItems { _ in
NavigationBar.ItemGroup(placement: .topBarLeading) {
NavigationBar.Buttons.closeEditor()
}
NavigationBar.ItemGroup(placement: .topBarTrailing) {
NavigationBar.Buttons.undo()
NavigationBar.Buttons.redo()
NavigationBar.Buttons.togglePagesMode()
NavigationBar.Buttons.export()
}
}

Photo Editor:

.imgly.navigationBarItems { _ in
NavigationBar.ItemGroup(placement: .topBarLeading) {
NavigationBar.Buttons.closeEditor()
}
NavigationBar.ItemGroup(placement: .topBarTrailing) {
NavigationBar.Buttons.undo()
NavigationBar.Buttons.redo()
NavigationBar.Buttons.togglePreviewMode()
NavigationBar.Buttons.export()
}
}

Video Editor:

.imgly.navigationBarItems { _ in
NavigationBar.ItemGroup(placement: .topBarLeading) {
NavigationBar.Buttons.closeEditor()
}
NavigationBar.ItemGroup(placement: .topBarTrailing) {
NavigationBar.Buttons.undo()
NavigationBar.Buttons.redo()
NavigationBar.Buttons.export()
}
}

Apparel Editor:

.imgly.navigationBarItems { _ in
NavigationBar.ItemGroup(placement: .topBarLeading) {
NavigationBar.Buttons.closeEditor()
}
NavigationBar.ItemGroup(placement: .topBarTrailing) {
NavigationBar.Buttons.undo()
NavigationBar.Buttons.redo()
NavigationBar.Buttons.togglePreviewMode()
NavigationBar.Buttons.export()
}
}

Postcard Editor:

.imgly.navigationBarItems { _ in
NavigationBar.ItemGroup(placement: .topBarLeading) {
NavigationBar.Buttons.closeEditor()
NavigationBar.Buttons.previousPage(
label: { _ in NavigationLabel("Design", direction: .backward) },
)
}
NavigationBar.ItemGroup(placement: .principal) {
NavigationBar.Buttons.undo()
NavigationBar.Buttons.redo()
NavigationBar.Buttons.togglePreviewMode()
}
NavigationBar.ItemGroup(placement: .topBarTrailing) {
NavigationBar.Buttons.nextPage(
label: { _ in NavigationLabel("Write", direction: .forward) },
)
NavigationBar.Buttons.export()
}
}

Modify Navigation Bar Items#

Use the .imgly.modifyNavigationBarItems modifier to adjust the default item list without rebuilding the entire configuration:

.imgly.modifyNavigationBarItems { context, items in

Parameters:

  • context - provides access to the engine, editor state, and event handler
  • items - mutable array of navigation bar item groups that can be modified

Available modification operations:

  • addFirst - prepends new items at the beginning of a placement group:
items.addFirst(placement: .topBarTrailing) {
NavigationBar.Button(id: "my.package.inspectorBar.button.first") { context in
print("First Button in top bar trailing placement group action")
} label: { context in
Label("First Button", systemImage: "arrow.backward.circle")
}
}
  • addLast - appends new items at the end of a placement group:
items.addLast(placement: .topBarLeading) {
NavigationBar.Button(id: "my.package.inspectorBar.button.last") { context in
print("Last Button in top bar leading placement group action")
} label: { context in
Label("Last Button", systemImage: "arrow.forward.circle")
}
}
  • addAfter - adds new items right after a specific item:
items.addAfter(id: NavigationBar.Buttons.ID.undo) {
NavigationBar.Button(id: "my.package.inspectorBar.button.afterUndo") { context in
print("After Undo")
} label: { context in
Label("After Undo", systemImage: "arrow.forward.square")
}
}
  • addBefore - adds new items right before a specific item:
items.addBefore(id: NavigationBar.Buttons.ID.redo) {
NavigationBar.Button(id: "my.package.inspectorBar.button.beforeRedo") { context in
print("Before Redo")
} label: { context in
Label("Before Redo", systemImage: "arrow.backward.square")
}
}
  • replace - replaces an existing item with new items:
items.replace(id: NavigationBar.Buttons.ID.closeEditor) {
NavigationBar.Buttons.closeEditor(
label: { _ in Label("Cancel", systemImage: "xmark") },
)
}
items.replace(id: NavigationBar.Buttons.ID.export) {
NavigationBar.Buttons.export(
label: { _ in Label("Done", systemImage: "checkmark") },
)
}
  • remove - removes an existing item:
items.remove(id: NavigationBar.Buttons.ID.togglePagesMode)

Each NavigationBar.Item requires a unique id for SwiftUI’s ForEach rendering. You have multiple options for creating navigation bar items, from simple predefined buttons to fully custom implementations. Items must be organized within NavigationBar.ItemGroup containers.

Use Predefined Buttons#

Start with predefined buttons from the NavigationBar.Buttons namespace. All available predefined buttons are listed below.

NavigationBar.Buttons.closeEditor()

Customize Predefined Buttons#

Customize any predefined button by overriding its default parameters:

NavigationBar.Buttons.undo(
action: { context in
try context.engine?.editor.undo()
},
label: { context in
Label { Text("Undo") } icon: { Image.imgly.undo }
.opacity(context.state.viewMode == .preview ? 0 : 1)
.labelStyle(.imgly.adaptiveIconOnly)
},
isEnabled: { context in
try !context.state.isCreating &&
context.state.viewMode != .preview &&
context.engine?.editor.canUndo() == true
},
isVisible: { context in true },
)

Available parameters:

  • action - the action to perform when the user triggers the button. Uses the engine to perform an undo step in this example.

  • label - the view that describes the purpose of the button’s action. Shows conditional opacity based on view mode in this example.

  • isEnabled - whether the button is enabled. This example checks if undo is available and editor state.

  • isVisible - whether the button should be visible. Can reserve layout space when hidden using the label view instead of this parameter.

Create New Buttons#

Create custom buttons when predefined options don’t meet your needs:

NavigationBar.Button(
id: "my.package.navigationBar.button.newButton",
) { context in
print("New Button action")
} label: { context in
Label("New Button", systemImage: "star.circle")
} isEnabled: { context in
true
} isVisible: { context in
true
}

Required and optional parameters:

  • id - the unique id of the button. This parameter is required.

  • action - the action to perform when the user triggers the button. This parameter is required.

  • label - a View that describes the purpose of the button’s action. Don’t encode visibility logic in this view. This parameter is required.

  • isEnabled - whether the button is enabled. By default, true is always used.

  • isVisible - whether the button should be visible. Can reserve layout space when hidden using the label view instead of this parameter. By default, true is always used.

Create New Custom Items#

For completely custom implementations, create a type conforming to the NavigationBar.Item protocol:

private struct CustomNavigationBarItem: NavigationBar.Item {
var id: EditorComponentID { "my.package.navigationBar.newCustomItem" }
func body(_ context: NavigationBar.Context) throws -> some View {
ZStack {
RoundedRectangle(cornerRadius: 10)
.fill(.conicGradient(colors: [.red, .yellow, .green, .cyan, .blue, .purple, .red], center: .center))
Text("New Custom Item")
.padding(4)
}
.onTapGesture {
print("New Custom Item action")
}
}
func isVisible(_ context: NavigationBar.Context) throws -> Bool {
true
}
}

Then use it in your navigation bar items:

CustomNavigationBarItem()

Protocol requirements:

  • var id: EditorComponentID { get } - the unique id of the item. This property is required.

  • func body(_: NavigationBar.Context) throws -> some View - the body of your view. Don’t encode visibility logic in this view unless layout space should be reserved when hidden. This property is required.

  • func isVisible(_: NavigationBar.Context) throws -> Bool - whether the item should be visible. Prefer using this parameter for visibility logic unless layout space should be reserved when hidden. By default, true is always used.

List of Available NavigationBar.Buttons#

All predefined buttons are available as static functions in the NavigationBar.Buttons namespace. Each function returns a NavigationBar.Button with default parameters that you can customize as shown in the Customize Predefined Buttons section.

ButtonIDDescription
NavigationBar.Buttons.closeEditorNavigationBar.Buttons.ID.closeEditorCloses editor via editor event .closeEditor.
NavigationBar.Buttons.undoNavigationBar.Buttons.ID.undoDoes undo operation in the editor via EditorAPI.undo engine API.
NavigationBar.Buttons.redoNavigationBar.Buttons.ID.redoDoes redo operation in the editor via EditorAPI.redo engine API.
NavigationBar.Buttons.exportNavigationBar.Buttons.ID.exportTriggers onExport callback via editor event .startExport.
NavigationBar.Buttons.togglePreviewModeNavigationBar.Buttons.ID.togglePreviewModeUpdates editor view mode via editor event .setViewMode: when current view mode is EditorViewMode.edit, then EditorViewMode.preview is set and vice versa. Note that this button is intended to be used in Photo Editor, Apparel Editor and Postcard Editor and may cause unexpected behaviors when used in other solutions.
NavigationBar.Buttons.togglePagesModeNavigationBar.Buttons.ID.togglePagesModeUpdates editor view mode via editor event .setViewMode: when current view mode is EditorViewMode.edit, then EditorViewMode.pages is set and vice versa. Note that this button is intended to be used in Design Editor and may cause unexpected behaviors when used in other solutions.
NavigationBar.Buttons.previousPageNavigationBar.Buttons.ID.previousPageNavigates to the previous page via editor event .navigateToPreviousPage.
NavigationBar.Buttons.nextPageNavigationBar.Buttons.ID.nextPageNavigates to the next page via editor event .navigateToNextPage.