【iOS】 Apple標準アプリ風にスクロールでナビゲーションタイトルを消す方法を共有したい

【iOS】 Apple標準アプリ風にスクロールでナビゲーションタイトルを消す方法を共有したい

2025.10.28

はじめに

スターバックス デジタルテクノロジー部のリルオッサです。

iOS 26がリリースされてから、Apple標準アプリを触りながらLiquid Glass時代のアプリの表現について色々と勉強しています。
その中で画面のコンテンツをスクロールするとナビゲーションタイトルが消えるような表現が多く採用されていることに気づき、どうやれば実現できるか調べてみることにしました。

そして、ついにその実現方法に辿り着くことができたので記事にすることにしました。
おまけで、ナビゲーションエリアに配置したツールバーアイテムの表示/非表示についてもチャレンジしてみたのでそちらも合わせてお楽しみください。

標準アプリの表現

https://www.youtube.com/watch?v=UqQuy2XLZFc

今回の実装デモ

一定量スクロールすると、ナビゲーションタイトルと右上ツールバーアイテムが非表示になります。

https://www.youtube.com/watch?v=P4SM4Tz6X58

スクロールするとナビゲーションタイトルを消す方法

Step 1: インラインのラージタイトルの基本実装

まずは標準アプリで多く見られるようになったインラインのラージタイトルを実装します。

コード

import SwiftUI

struct SampleView: View {
    var body: some View {
        NavigationStack {
            ScrollView {
                ForEach(0..<100) {
                    Text("\($0)")
                        .frame(maxWidth: .infinity)
                        .padding(.vertical)
                        .background(
                            .yellow.gradient,
                            in: .rect(cornerRadius: 16)
                        )
                        .padding(4)
                }
            }
            .navigationTitle("ホーム")
            .toolbarTitleDisplayMode(.inlineLarge) // 👈 ここがポイント
        }
    }
}

解説

iOS 17から追加されているToolbarTitleDisplayModeを使用してタイトルの表示状態を設定することができます。
その表示モードの中にinlineLargeというプロパティが存在しており、これを使用すると、インライン表示かつ大きいタイトルでナビゲーションエリアに表示することができます。

Step 2: スクロール時にタイトルを非表示にする

コード

struct SampleView: View {
    var body: some View {
        NavigationStack {
            ScrollView {
                // ... 省略
            }
            .navigationTitle("ホーム")
            .toolbarTitleDisplayMode(.inlineLarge)
            .toolbar {
                ToolbarItem(placement: .title) { // 👈 ここがポイント
                    Text("") // 👈 ここがポイント
                }
            }
        }
    }
}

デモ

上記のコードの場合、ナビゲーションタイトルがインライン状態に切り替わるタイミングで非表示になります。

https://www.youtube.com/watch?v=IGcoDyyw378

解説

ツールバーにボタンなどを配置する時に使用するでお馴染みなToolbarItemですが、このツールバーの配置場所を指定するToolbarPlacementにはtitleというプロパティがあります。

そのtitleDiscussionには以下のような記述があります。

The view will be shown when the navigation bar renders its title inline, and takes precedence over the value provided to the View.navigationTitle(_:) modifier.

これを使用すると、ナビゲーションバーがタイトルをインラインで描画するときに対象のViewが表示され、View.navigationTitle(_:) モディファイアに指定された値よりも優先されることが分かりました。

そこでインライン表示された際は、タイトルを空文字に変更することでナビゲーションタイトルが非表示になる挙動を表現することができました。

その他の成功実装例

空文字以外でも試してみました。

.toolbar {
    ToolbarItem(placement: .title) {
        Text("ホーム")
            .hidden()
    }
}

Text("…").hidden()でも同様にインライン表示になった際にタイトルを消すことができました。

コードを見た時に明示的にタイトルが非表示になっていることが伝わるので、この記述方法もいいかもしれません。

その他では、Color.clearを指定する方法でも再現することができました。

.toolbar {
    ToolbarItem(placement: .title) {
        Color.clear
    }
}

その他の失敗実装例

思いついて試してみたのですが、失敗した例も紹介します。

.toolbar {
    ToolbarItem(placement: .title) {
        // 何も記載しない
    }
}
.toolbar {
    ToolbarItem(placement: .title) {
        EmptyView()
    }
}

こちらの失敗する方法では、ナビゲーションタイトルがインラインになっても消えず、通常通りnavigationTitleの値が表示されました。

Step 3: スクロール量に応じてツールバーアイテムを制御する

ここからはおまけとして、スクロール量を検知して、特定の位置でツールバーアイテムを表示/非表示する実装を紹介します。

※ 実装のデモは記事トップの今回の実装デモ動画を参照してください。

コード

struct SampleView: View {
    @State private var isToolbarItemVisible = false

    var body: some View {
        NavigationStack {
            ScrollView {
                // ...省略
            }
            .navigationTitle("ホーム")
            .toolbarTitleDisplayMode(.inlineLarge)
            .toolbar {
                ToolbarItem(placement: .title) {
                    Text("")
                }

                if isToolbarItemVisible {
                    ToolbarItem(placement: .primaryAction) {
                        Image(.littleOssa)
                            .clipShape(.circle)
                    }
                    .sharedBackgroundVisibility(.hidden)
                }
            }
            .onScrollGeometryChange(for: Bool.self) { geometry in
                geometry.contentOffset.y < -100
            } action: { _, shouldShowToolbarItem in
                isToolbarItemVisible = shouldShowToolbarItem
            }
        }
    }
}

解説

ツールバーアイテムの条件付き表示

標準アプリのアカウントボタンを模倣した画像をツールバーアイテムとして配置しています。

.toolbar {
    ToolbarItem(placement: .title) {
        Text("")
    }

    // 表示フラグがtrueの時のみ表示されるツールバーアイテム
    if isToolbarItemVisible {
        ToolbarItem(placement: .primaryAction) {
            Image(.littleOssa)
                .clipShape(.circle)
        }
        .sharedBackgroundVisibility(.hidden)
    }
}

isToolbarItemVisibleフラグを使って、条件に応じてツールバーアイテムを表示/非表示しています。

sharedBackgroundVisibility(_:)について

このモディファイアは、ツールバー内の項目のガラス背景効果の表示/非表示を制御するために使用しています。.hiddenを指定することでガラス背景効果を非表示にし、画像のみを表示することができます。

スクロール量の取得と判定

iOS 18から追加されたonScrollGeometryChangeを使用することで、スクロール位置を監視できます。

.onScrollGeometryChange(for: Bool.self) { geometry in
    geometry.contentOffset.y < -100
} action: { _, shouldShowToolbarItem in
    isToolbarItemVisible = shouldShowToolbarItem
}

contentOffset.yの仕組み

ScrollGeometrycontentOffset.yは、スクロールビュー全体のコンテンツサイズ内でのy軸の位置を表します。

重要なポイントは、スクロール開始位置(最上部)でマイナスの値から始まり、スクロールを進めていくと0に近づき、さらに続けるとプラスになるということです。

今回はgeometry.contentOffset.y < -100という条件を設定していますが、これは大きなタイトルがまだ十分に表示されている状態からインラインに変わるタイミングを標準アプリの挙動を参考にしながら実際に触って調整して決めました。

この値はお好みに応じて調整してみてください。自分のアプリに合った値を見つけるのがおすすめです。

おわりに

この方法を発見できた時は大興奮でしたが、ナビゲーションタイトルの部分については、iOS 17以下のAPIから実現できることがわかり、今まで辿り着けず悔しい気持ちです。

個人的な考察ではありますが、NavigationBarItem.TitleDisplayModeではなく、ToolbarTitleDisplayModeinlineLargeが追加されていることや、多くの標準アプリがこのinlineLargeを採用しているところを見ると、近い将来はnavigationBarTitleDisplayMode(_:)が非推奨になっていくかもしれないですね。

標準アプリを触っているとたくさん気づきを得れるので引き続きたくさん触っていきたいと思います。

iOSエンジニア募集中

スターバックス デジタルテクノロジー部では、iOSアプリ開発のできるエンジニアを募集しています。
Liquid Glassの新しいデザインやアプリ開発についてワイワイしながら一緒に働いてくれる仲間をお待ちしています☺︎

https://careers.classmethod.jp/requirements/sbj-nativeapp-ios/

参考

https://developer.apple.com/documentation/swiftui/toolbartitledisplaymode
https://developer.apple.com/documentation/swiftui/toolbartitledisplaymode/inlinelarge
https://developer.apple.com/documentation/swiftui/toolbaritemplacement/title
https://developer.apple.com/documentation/swiftui/view/onscrollgeometrychange(for:of:action:)
https://developer.apple.com/documentation/swiftui/scrollgeometry

この記事をシェアする

FacebookHatena blogX

関連記事