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 Architecture#
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 toNavigationBar.Button
- Pre-built button implementation with action and labelNavigationBar.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 whenisVisible(_:)
returnstrue
. -
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:
.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() }}
.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() }}
.imgly.navigationBarItems { _ in NavigationBar.ItemGroup(placement: .topBarLeading) { NavigationBar.Buttons.closeEditor() } NavigationBar.ItemGroup(placement: .topBarTrailing) { NavigationBar.Buttons.undo() NavigationBar.Buttons.redo() NavigationBar.Buttons.export() }}
.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() }}
.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 handleritems
- 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)
NavigationBar.Item Configuration#
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
- aView
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.
Button | ID | Description |
---|---|---|
NavigationBar.Buttons.closeEditor | NavigationBar.Buttons.ID.closeEditor | Closes editor via editor event .closeEditor . |
NavigationBar.Buttons.undo | NavigationBar.Buttons.ID.undo | Does undo operation in the editor via EditorAPI.undo engine API. |
NavigationBar.Buttons.redo | NavigationBar.Buttons.ID.redo | Does redo operation in the editor via EditorAPI.redo engine API. |
NavigationBar.Buttons.export | NavigationBar.Buttons.ID.export | Triggers onExport callback via editor event .startExport . |
NavigationBar.Buttons.togglePreviewMode | NavigationBar.Buttons.ID.togglePreviewMode | Updates 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.togglePagesMode | NavigationBar.Buttons.ID.togglePagesMode | Updates 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.previousPage | NavigationBar.Buttons.ID.previousPage | Navigates to the previous page via editor event .navigateToPreviousPage . |
NavigationBar.Buttons.nextPage | NavigationBar.Buttons.ID.nextPage | Navigates to the next page via editor event .navigateToNextPage . |