SwiftUIでアプリを開発していると、ナビゲーションバーにボタンやメニューを追加したい場面がよくあります。そんなときに活躍するのがToolbarItem
です。
本記事では、SwiftUI初心者の方でもすぐに使えるよう、ToolbarItem
の基本から実践的な使い方まで、豊富なサンプルコード付きで詳しく解説します。
ToolbarItemとは?
ToolbarItem
は、SwiftUIでナビゲーションバーやツールバーにボタンなどのUI要素を配置するための機能です。iOS、iPadOS、macOSなど、各プラットフォームに適した位置に自動的に配置してくれます。
こんな場面で使います
- ナビゲーションバーに「保存」「編集」ボタンを追加
- 右上にメニューアイコンを配置
- リストに「追加」ボタンを設置
- 設定画面への遷移ボタンを実装
基本的な使い方
まずは最もシンプルな使い方から見ていきましょう。
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
Text("メインコンテンツ")
.navigationTitle("ホーム")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("追加") {
print("追加ボタンがタップされました")
}
}
}
}
}
}
この例では、ナビゲーションバーの右上に「追加」ボタンが表示されます。
基本構文の説明
.toolbar {
ToolbarItem(placement: .配置位置) {
// ここにボタンやビューを配置
}
}
toolbar
: ツールバーを定義するモディファイアToolbarItem
: ツールバー内の個々のアイテムplacement
: アイテムを配置する位置を指定
配置位置(placement)の種類と使い分け
placement
パラメータで、ToolbarItemをどこに配置するか指定できます。よく使う配置位置を見ていきましょう。
navigationBarTrailing(右上)
ナビゲーションバーの右側に配置されます。最もよく使われる位置です。
struct TrailingExample: View {
var body: some View {
NavigationView {
Text("コンテンツ")
.navigationTitle("サンプル")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
print("保存")
}) {
Image(systemName: "square.and.arrow.down")
}
}
}
}
}
}
使用例: 保存ボタン、共有ボタン、設定ボタン
navigationBarLeading(左上)
ナビゲーションバーの左側に配置されます。
struct LeadingExample: View {
var body: some View {
NavigationView {
Text("コンテンツ")
.navigationTitle("サンプル")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("キャンセル") {
print("キャンセル")
}
}
}
}
}
}
使用例: キャンセルボタン、メニューボタン、戻るボタンの補助
principal(中央)
ナビゲーションバーの中央に配置されます。カスタムタイトルを表示したい場合に便利です。
struct PrincipalExample: View {
var body: some View {
NavigationView {
Text("コンテンツ")
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
Text("カスタムタイトル")
.font(.headline)
Text("サブタイトル")
.font(.caption)
.foregroundColor(.gray)
}
}
}
}
}
}
使用例: カスタムデザインのタイトル、検索バー
primaryAction(プライマリアクション)
プラットフォームに応じた主要な操作位置に配置されます。
struct PrimaryActionExample: View {
var body: some View {
NavigationView {
Text("コンテンツ")
.navigationTitle("タスク")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button("完了") {
print("完了")
}
}
}
}
}
}
使用例: 完了ボタン、送信ボタン
複数のToolbarItemを配置する
複数のボタンをツールバーに配置することもできます。
import SwiftUI
struct MultipleItemsView: View {
@State private var showAlert = false
@State private var favorited = false
var body: some View {
NavigationView {
List(1...20, id: \.self) { number in
Text("項目 \(number)")
}
.navigationTitle("リスト")
.toolbar {
// 左側にメニューボタン
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
print("メニュー")
}) {
Image(systemName: "line.3.horizontal")
}
}
// 右側に追加ボタン
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
print("追加")
}) {
Image(systemName: "plus")
}
}
// 右側にお気に入りボタン
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
favorited.toggle()
}) {
Image(systemName: favorited ? "star.fill" : "star")
.foregroundColor(favorited ? .yellow : .gray)
}
}
// 右側に設定ボタン
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showAlert = true
}) {
Image(systemName: "gear")
}
}
}
.alert("設定", isPresented: $showAlert) {
Button("OK") { }
}
}
}
}
このように、複数のToolbarItem
を記述することで、様々なボタンを配置できます。
ToolbarItemGroupで複数をまとめる
関連する複数のアイテムをグループ化したい場合は、ToolbarItemGroup
を使います。
import SwiftUI
struct ToolbarGroupExample: View {
var body: some View {
NavigationView {
Text("コンテンツ")
.navigationTitle("写真")
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(action: {
print("共有")
}) {
Image(systemName: "square.and.arrow.up")
}
Button(action: {
print("お気に入り")
}) {
Image(systemName: "heart")
}
Button(action: {
print("その他")
}) {
Image(systemName: "ellipsis.circle")
}
}
}
}
}
}
ToolbarItemGroup
を使うと、複数のボタンを1つのグループとして管理でき、コードも読みやすくなります。
実践例1: 編集可能なリスト
実際のアプリでよく使われる、編集モード付きリストの実装例です。
import SwiftUI
struct EditableListView: View {
@State private var items = ["りんご", "バナナ", "オレンジ", "ぶどう", "いちご"]
@State private var editMode: EditMode = .inactive
@State private var showingAddAlert = false
@State private var newItemName = ""
var body: some View {
NavigationView {
List {
ForEach(items, id: \.self) { item in
Text(item)
}
.onDelete(perform: deleteItems)
.onMove(perform: moveItems)
}
.navigationTitle("フルーツリスト")
.environment(\.editMode, $editMode)
.toolbar {
// 左側: 編集ボタン
ToolbarItem(placement: .navigationBarLeading) {
Button(editMode == .active ? "完了" : "編集") {
withAnimation {
editMode = editMode == .active ? .inactive : .active
}
}
}
// 右側: 追加ボタン
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showingAddAlert = true
}) {
Image(systemName: "plus.circle.fill")
}
.disabled(editMode == .active)
}
}
.alert("新しいフルーツを追加", isPresented: $showingAddAlert) {
TextField("フルーツ名", text: $newItemName)
Button("追加") {
if !newItemName.isEmpty {
items.append(newItemName)
newItemName = ""
}
}
Button("キャンセル", role: .cancel) {
newItemName = ""
}
}
}
}
func deleteItems(at offsets: IndexSet) {
items.remove(atOffsets: offsets)
}
func moveItems(from source: IndexSet, to destination: Int) {
items.move(fromOffsets: source, toOffset: destination)
}
}
この例では、編集モードの切り替えと、新しいアイテムの追加機能を実装しています。
実践例2: メモアプリ風のツールバー
メモアプリのような、充実したツールバーの実装例です。
import SwiftUI
struct NoteEditorView: View {
@State private var noteText = ""
@State private var showingShare = false
@State private var isBold = false
@State private var isItalic = false
var body: some View {
NavigationView {
VStack {
TextEditor(text: $noteText)
.padding()
}
.navigationTitle("新規メモ")
.toolbar {
// 左側: 閉じるボタン
ToolbarItem(placement: .navigationBarLeading) {
Button("閉じる") {
print("閉じる")
}
}
// 右側: 操作ボタングループ
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button(action: {
showingShare = true
}) {
Image(systemName: "square.and.arrow.up")
}
Button("保存") {
print("保存しました")
}
}
// 下部: フォーマットツール(iOS 16+)
ToolbarItemGroup(placement: .bottomBar) {
Button(action: {
isBold.toggle()
}) {
Image(systemName: "bold")
.foregroundColor(isBold ? .blue : .gray)
}
Button(action: {
isItalic.toggle()
}) {
Image(systemName: "italic")
.foregroundColor(isItalic ? .blue : .gray)
}
Spacer()
Button(action: {
print("画像を追加")
}) {
Image(systemName: "photo")
}
Button(action: {
print("チェックリスト追加")
}) {
Image(systemName: "checklist")
}
}
}
.sheet(isPresented: $showingShare) {
Text("共有画面")
}
}
}
}
カスタムボタンのデザイン
ToolbarItemの中で、カスタムデザインのボタンを作成することもできます。
import SwiftUI
struct CustomToolbarButtonView: View {
@State private var notificationCount = 5
var body: some View {
NavigationView {
Text("コンテンツ")
.navigationTitle("ホーム")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
print("通知を確認")
}) {
ZStack(alignment: .topTrailing) {
Image(systemName: "bell.fill")
.font(.title3)
if notificationCount > 0 {
Text("\(notificationCount)")
.font(.caption2)
.foregroundColor(.white)
.padding(4)
.background(Color.red)
.clipShape(Circle())
.offset(x: 8, y: -8)
}
}
}
}
}
}
}
}
このように、バッジ付きの通知アイコンなど、カスタムデザインも実装できます。
プラットフォーム別の表示の違い
ToolbarItem
の大きな利点は、各プラットフォームに適した位置に自動配置されることです。
iOS/iPadOS
navigationBarTrailing
: ナビゲーションバーの右上navigationBarLeading
: ナビゲーションバーの左上bottomBar
: 画面下部のツールバー
macOS
- ウィンドウ上部のツールバーに表示
- より多くのアイテムを横並びで表示可能
watchOS
- 画面サイズに応じて最適化された位置に表示
- シンプルなアイコンが推奨される
同じコードで、各プラットフォームに最適なUIを実現できます。
よくあるエラーと解決方法
エラー1: NavigationViewがない
// ❌ 悪い例:NavigationViewなしでtoolbarを使用
struct ContentView: View {
var body: some View {
Text("コンテンツ")
.toolbar { // これは表示されない
ToolbarItem(placement: .navigationBarTrailing) {
Button("保存") { }
}
}
}
}
// ✅ 良い例:NavigationViewで囲む
struct ContentView: View {
var body: some View {
NavigationView {
Text("コンテンツ")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("保存") { }
}
}
}
}
}
toolbar
モディファイアは、NavigationView
またはNavigationStack
内で使用する必要があります。
エラー2: ボタンが表示されない
// ❌ 悪い例:navigationTitleがない
NavigationView {
Text("コンテンツ")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("保存") { }
}
}
}
// ✅ 良い例:navigationTitleを設定
NavigationView {
Text("コンテンツ")
.navigationTitle("ホーム") // これが重要
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("保存") { }
}
}
}
ナビゲーションバーを表示するには、navigationTitle
の設定が必要です。
エラー3: @Stateの宣言忘れ
// ❌ 悪い例:状態管理ができない
var isEditing = false
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("編集") {
isEditing.toggle() // ビューが更新されない
}
}
}
// ✅ 良い例:@Stateを使用
@State private var isEditing = false
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("編集") {
isEditing.toggle() // ビューが正しく更新される
}
}
}
NavigationStack(iOS 16+)での使用
iOS 16以降では、NavigationView
の代わりにNavigationStack
を使用できます。
import SwiftUI
struct NavigationStackExample: View {
var body: some View {
NavigationStack {
List(1...20, id: \.self) { number in
NavigationLink("項目 \(number)", value: number)
}
.navigationTitle("リスト")
.navigationDestination(for: Int.self) { number in
Text("項目 \(number)の詳細")
.navigationTitle("詳細")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("共有") {
print("共有")
}
}
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("設定") {
print("設定")
}
}
}
}
}
}
NavigationStack
を使うと、より現代的なナビゲーションが実装できます。
パフォーマンスのベストプラクティス
ToolbarItemを効率的に使うためのポイントをまとめます。
1. 適切な数のアイテムを配置
// ✅ 良い例:必要最小限のボタン(3〜4個程度)
.toolbar {
ToolbarItemGroup(placement: .navigationBarTrailing) {
Button("共有") { }
Button("編集") { }
Button("削除") { }
}
}
// ❌ 悪い例:ボタンが多すぎる(5個以上)
// ユーザーが混乱する可能性がある
2. SF Symbolsの活用
// ✅ 良い例:わかりやすいアイコンを使用
Button(action: {}) {
Image(systemName: "square.and.arrow.up") // 共有アイコン
}
// より良い例:アクセシビリティラベルを追加
Button(action: {}) {
Image(systemName: "square.and.arrow.up")
}
.accessibilityLabel("共有")
3. 状態に応じた表示制御
@State private var isProcessing = false
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
if isProcessing {
ProgressView()
} else {
Button("保存") {
isProcessing = true
// 保存処理
}
}
}
}
まとめ
ToolbarItem
は、SwiftUIでナビゲーションバーやツールバーを簡単にカスタマイズできる強力な機能です。
覚えておきたいポイント
NavigationView
またはNavigationStack
内で使用するplacement
パラメータで配置位置を指定できる- 複数のアイテムは
ToolbarItemGroup
でまとめられる - プラットフォームごとに最適な位置に自動配置される
- SF Symbolsを使うと美しいアイコンが簡単に使える
@State
で状態管理を忘れずに
主要な配置位置まとめ
placement | 位置 | 用途 |
---|---|---|
.navigationBarTrailing | 右上 | 保存、共有、設定ボタン |
.navigationBarLeading | 左上 | キャンセル、メニューボタン |
.principal | 中央 | カスタムタイトル、検索バー |
.primaryAction | プライマリ位置 | 完了、送信ボタン |
.bottomBar | 下部 | フォーマットツール、追加機能 |
この記事で紹介したサンプルコードを実際にXcodeで動かして、ToolbarItemの使い方をマスターしましょう。ナビゲーションバーのカスタマイズは、アプリの使いやすさを大きく左右する重要な要素です!