Tabelog Tech Blog

食べログの開発者による技術ブログです

食べログアプリのXcode 26/iOS 26対応 〜実例集〜

この記事は 食べログアドベントカレンダー2025 の4日目の記事です🎅🎄


こんにちは!食べログのアプリ基盤チームでiOSのリードをしている大澤です。

2025年9月にXcode 26がリリースされ、iOS 26の新機能や変更点への対応が必要になりました。 今回のメジャーバージョンアップでは、特にiOS 26で導入されたリキッドグラス(Liquid Glass)デザインへの対応が大きな課題となりました。 iOS 7のフラットデザイン導入以来、これほど大きなUIの変更はなかったと言えるでしょう。

本記事では、食べログアプリで実際に発生した問題や警告と、その具体的な修正方法をコード例とともに紹介します。Xcode 26/iOS 26対応で困っている方の参考になれば幸いです。

目次

はじめに

2025年9月、AppleはXcode 26をリリースし、iOS 26の新機能が利用可能になりました。iOS 26では、リキッドグラスと呼ばれる新しいデザインスタイルが導入され、アプリの見た目や操作感に大きな変化がもたらされました。

今回のメジャーバージョンアップでは、iOS 26のリキッドグラスデザインに適用させるため、多くのUIコンポーネントの調整が必要となりました。 食べログアプリはUIKit主体のアプリケーションで、長年の開発の歴史を持っています。 そのため、本記事ではUIKitベースの対応に焦点を当てています。 SwiftUIで開発されている方には申し訳ございませんが、ご了承ください。

本記事では、食べログアプリで実際に発生した問題とその解決策を実例として紹介します。特に、以下のようなテーマに関心のある方の参考になればと思います。

  • iOS 26のリキッドグラスデザインへの適用方法
  • 既存のUIKitアプリを新しいデザインシステムに合わせる際の注意点

対応方針

対応スケジュール

食べログアプリのXcode 26/iOS 26対応は、プロダクト開発とは並行して基盤チーム側で2025年8月からベータ版をもとに検証・開発を進め、約3ヶ月をかけて準備を進めました。 執筆時点では、リリース準備中ですが順調であれば12月上旬にリリースを予定しています。

対応の優先順位は以下の通りです。

  1. ビルドエラーの解消:Xcode 26でビルドできない問題を最優先で解決。ビルドが通らないサードパーティライブラリの更新などを実施
  2. 警告の解消:非推奨APIの使用や、iOS 26で変更された動作に関する警告を解消。ConcurrencyやSwift-Testingの記述で一部警告が出ていたため修正
  3. リキッドグラスデザインへの適用:UIコンポーネントを個別に適用。ほとんどがこちらへの対応のため、本稿でも主にこちらについて言及します

ビルドエラーの解消や警告の解消については普段のXcodeアップデートと大きな違いはなく、ライブラリの更新や該当するコードの修正で対応できました。 特に、食べログアプリでは今年の夏にSwift 6対応をすでに実施しており、そのこともあり大きな問題なく対応できました。

リキッドグラスへの適用範囲

食べログアプリでは、以下の方針で対応しました。

  • 完全適用は見送り:iOS 18以下のデザインとの整合性を保つため、完全なリキッドグラス化は行わない

なお、Info.plistに以下のKeyを設定することでおよそ1年間は暫定的にiOS 26上でも互換モードで動作させることができますが、今回は対応せず早くからリキッドグラスに適合させることを重視しました。 もし、iOS 26へのデザイン適用は後回しにしてXcode 26でのビルドを優先している場合は、こちらの採用を検討すると良いでしょう。 参考:UIDesignRequiresCompatibility 公式リファレンス

<key>UIDesignRequiresCompatibility</key>
<true/>
  • 一部コンポーネントへの対応:FABボタンなどの適用しやすいコンポーネントや、標準UIで構築されている箇所は適用させています。 一方で、リキッドグラスデザインの本来の目的はコンテンツに焦点をあてることですが、標準のままナビゲーションを使用するとその箇所だけが逆に視覚的に目立ってしまいます。 そのため、前述の整合性を保つため大規模なナビゲーションの修正は行わず、ナビゲーションバー上のボタンはリキッドグラス効果を無効化する対応をしました。

Xcode 26/iOS 26で発生した主要な問題と解決策

基本要素のサイズ・位置調整

iOS 26では、リキッドグラスデザインに合わせて、多くのUIコンポーネントのデフォルトサイズや位置が変更されました。

問題例:ナビゲーション

リキッドグラス化によりサイズが変更され、従来のボタンは44x44ptでしたが、iOS 26ではボタンのコンテンツ部分は36x44pt、リキッドグラスのエフェクトを含めたサイズは44x52ptとなっています。 また、ボタンのサイズに合わせてナビゲーションバーの高さも44ptから54ptとなっています。

ナビゲーションバーとボタンのサイズ比較

これは、長年変わらないものとしてナビゲーションを暗黙的に44ptの高さとして考えてきたiOSエンジニアには衝撃的な変更です。 幸いにも食べログアプリでは大きな影響はありませんでしたが、もし高さをハードコードして画面構築している場合は注意が必要です。 一方で、ボタンのコンテンツサイズが変更されたことで見切れが生じ、一部のボタンで位置調整を実施しました。 詳細については後述するナビゲーションバーボタンのリキッドグラス無効化について記載します。

問題例:テーブル

従来、UITableViewCellのデフォルトの高さは44ptでしたが、iOS 26では52ptとなっています。 多くの画面では問題ありませんでした。 しかし、前述のナビゲーションバーの高さ変更も加わり、一部のハーフモーダルのUIでコンテンツが収まりきらない問題を確認しました。 そのため、ハーフモーダルの高さを調整する対応をしました。

問題例:スイッチ

食べログアプリではトグルのスイッチとして標準のUISwitchを使用しており、従来では51x31ptでしたが、iOS 26では61x28ptとなっています。 オートレイアウトの設定によってはレイアウト崩れを引き起こす可能性があるため、使用している箇所は注意が必要です。 食べログアプリでも、オートレイアウトの設定が適切でなかった箇所で崩れを確認したため、調整しました。

FABボタンのリキッドグラス化

食べログアプリでは、FABボタン(フローティングアクションボタン)を一部画面で採用しています。iOS 26では、UIButtonに簡単にリキッドグラスエフェクトを適用する新しいAPIが追加されました。

floatingActionButton.configurationUpdateHandler = { button in
    var configuration: UIButton.Configuration
    if #available(iOS 26.0, *) {
        configuration = .glass() // 色付きボタンの場合は`.prominentGlass()`を採用し、`baseBackgroundColor`で背景色を設定
        // 角丸の変更は自動で対応される
    } else {
        configuration = .plain()
        configuration.background.backgroundColor = .white
        configuration.cornerStyle = .capsule
    }
    // 
    // 文字列やフォント、画像を設定する(ここでは省略)
    // 
    button.configuration = configuration
}

以下は、実際にFABボタンにリキッドグラスを適用した比較です。左がリキッドグラス適用前、右が適用後です。

FABボタンのリキッドグラス適応前後の比較

タブバー

iOS 26でのリキッドグラスエフェクトが適用され、一番影響が大きかった箇所です。食べログアプリでは、2つの大きな問題が発生しました。

問題例:タブバーへの独自バッジ表示

iOS 26ではタブバーの構造が大きく変化しています。 これにより、標準APIであるbadgeValueを使用してバッジ表示している場合は問題ありませんが、addSubviewでカスタムViewをバッジとして表示している場合は問題が発生します。

# ~iOS 18
/- UITabBar
  |- [UITabBarButton]
    |- UITabBarSwappableImageView
    |- UITabBarButtonLabel

# iOS 26
/- UITabBar
  |- _UITabBarPlatterView
    |- SelectedContentView
      |- [_UITabButton]
        |- UIImageView
        |- UILabel
    |- ContentView
      |- [_UITabButton]
        |- UIImageView
        |- UILabel

構造が複雑になっていることがわかります。 単純にaddSubviewする場合でも階層位置が変わっており、また、タブの選択時と非選択時でViewが分かれているのも特徴です。 食べログでは以下のようなコードで処理を分けて対応しました。

func setupIOS26Before(tabIndex: Int) {
    // 初期化は省略
    let customBadgeView: UIView

    // ImageViewのsubviewにバッジを追加
    if let tabIconImageView = tabBar.subviews[index].subviews.first(where: { $0 is UIImageView }) {
        tabIconImageView.addSubview(customBadgeView)
        // 位置調整(省略)
    }
}

@available(iOS 26.0, *)
func setupIOS26Later(tabIndex: Int) {
    // 初期化は省略
    let selectedCustomBadgeView: UIView
    let deselectedCustomBadgeView: UIView

    // 選択/非選択の両方のImageViewのsubviewにバッジを追加
    if let selectedTabView = tabBar.subviews.first?.subviews[0].subviews[tabIndex],
        let tabIconImageView = selectedTabView.subviews.first(where: { $0 is UIImageView }) {
            tabIconImageView.addSubview(selectedCustomBadgeView)
            // 位置調整(省略)
    }
    if let deselectedTabView = tabBar.subviews.first?.subviews[2].subviews[tabIndex],
        let tabIconImageView = deselectedTabView.subviews.first(where: { $0 is UIImageView }) {
            tabIconImageView.addSubview(deselectedCustomBadgeView)
            // 位置調整(省略)
    }
}

注意点として、subviewsへのアクセスをハードコードしている箇所があり、SDKやOSのバージョンアップによっては動作しなくなる可能性があります。 もしバージョンアップする際は、確実に動作することを確認してから対応しましょう。

問題例:コンテンツ内でのタブバーの使用

UITabBarControllerのように画面全体をコントロールするタブバーは問題ありませんが、コンテンツを切り替えるためのタブデザインとして部分的に標準のUITabBarを使用している場合は注意が必要です。 食べログアプリでは一部画面でページ内の要素を切り替えるためにUITabBarを使用していましたが、こちらにもリキッドグラスデザインが適用され、違和感のあるUIとなってしまいました。

UITabBarを従来のデザインにカスタマイズする方法は見つからなかったため、食べログではiOS 18以下との整合性も考慮してUITabBarの使用をやめ、独自のViewを作成することで対応しました。 一般的には車輪の再発明など好ましくないケースですが、許容できる範囲として対応しました。

ナビゲーションバーのボタン

食べログアプリではUINavigationBarButtonItemを使用しており、主に以下の3種類の使用方法があります。

  • 画像を設定
  • 文字を設定
  • カスタムなViewを設定

今回、食べログアプリではリキッドグラスの適用範囲のセクションでも記載しましたが、ナビゲーションバーのボタンは基本的にリキッドグラスを無効化する対応をしています。 hidesSharedBackgroundというプロパティが追加されているので、こちらをtrueに設定することで対応できます。

しかし、そのまま無効化しただけでは単純にリキッドグラスのエフェクト部分の余白が残り、前述したサイズ変更のように大きくなってしまいます。 画像や文字だけを設定したボタンはどうしても横幅の違和感が強くなったため、試行錯誤したところ、customViewとして扱うことで最小44ptまで緩和できることを発見しました。 以下のような実装をしたボタンを作成して対応しました。

class NoLiquidGlassBarButtonItem: UIBarButtonItem {
    @available(iOS 26.0, *)
    override var hidesSharedBackground: Bool {
        get {
            // 強制的にtrueを返す
            return true
        }
        set {
            // 何もしない
        }
    }

    override var tintColor: UIColor? {
        didSet {
            // 文字だけの場合、実際にはcustomViewのボタンへの反映が必要なのでoverrideして対応
            if let button = customView as? UIButton {
                button.tintColor = tintColor
            }
        }
    }

    @available(iOS 26.0, *)
    static func makeForIOS26(title: String?, target: Any?, action: Selector?) -> Self {
        let button = createButton(target: target, action: action)
        button.setTitle(title, for: .normal)

        let barButtonItem = Self()
        barButtonItem.customView = button
        return barButtonItem
    }

    @available(iOS 26.0, *)
    static func makeForIOS26(image: UIImage?, target: Any?, action: Selector?) -> Self {
        let button = createButton(target: target, action: action)
        button.setImage(image, for: .normal)

        let barButtonItem = Self()
        barButtonItem.customView = button
        return barButtonItem
    }

    private static func createButton(target: Any?, action: Selector?) -> UIButton {
        let button = UIButton(type: .system)
        if let action {
            button.addTarget(target, action: action, for: .touchUpInside)
        }
        button.titleLabel?.font = .systemFont(ofSize: 17.0, weight: .medium) // デフォルトのフォントサイズ

        NSLayoutConstraint.activate([
            button.widthAnchor.constraint(greaterThanOrEqualToConstant: 36.0), // 実際にはマージン込みで44以上のサイズになる
            button.heightAnchor.constraint(equalToConstant: 44.0),
        ])
        return button
    }
}

注意点として、あくまでリキッドグラスの見栄えだけを無効化しており、画面遷移時のアニメーションなどはそのままです。

スクロールビューのエッジエフェクト無効化

iOS 26ではリキッドグラスデザインに合わせて、ナビゲーションの下にコンテンツがスクロールされていくことを想定してエッジエフェクトが追加されています。 視認しづらいですが、よく見ると薄い白色のブラーが付いています。

食べログアプリでは一部画面で既存からヘッダ部分にブラーをつけており、このエフェクトと競合して見づらくなっていたことから、該当画面でのエフェクトを無効化しました。

if #available(iOS 26.0, *) {
    scrollView.topEdgeEffect.isHidden = true
}

アプリアイコンの変更

iOS 26では、アプリアイコンのデザインガイドラインが更新されました。食べログアプリでは、新しいガイドラインに準拠したアプリアイコンへ対応しました。

  1. アプリアイコンの再作成:デザイナー側で新しいガイドラインに準拠したアプリアイコンをIconComposerを使用して作成(.icon形式)
  2. Assetsカタログからの移行:Xcodeのプロジェクトナビゲータにアイコンファイルをドラッグ&ドロップするだけでOK。従来通りASSETCATALOG_COMPILER_APPICON_NAMEにアイコンファイルの名称を設定
    • アイコンファイルがAppIcon.iconの場合はAppIconを設定

注意点として、アプリアイコンをリキッドグラスデザインに合わせると、iOS 18以下の端末でもアイコンが変更されてしまいます。 デバイスのOSごとの出し分けはできないので、よく検討してから対応しましょう。

課題

スワイプバックの対応

iOS 26では、スワイプバックの動作が改善され、画面端からだけでなく戻ることができるようになりました。 一方、食べログアプリでは戻るボタンをカスタマイズしていることからそのままでは有効化されません。 また、一部画面では戻る処理をカスタマイズしており、そこでは正しく動作しない問題も起きていました。 そのため、今回の対応では見送っています。

もし戻るボタンをカスタマイズしていて有効化したい場合は新規追加されたinteractiveContentPopGestureRecognizerを一度試してみると良いでしょう。

HyperionによるViewデバッグ

食べログアプリでは、開発時にHyperionというViewデバッグツールを使用しています。 一方で、iOS 26ではタブバーの扱いが大きく変わり、Window全体を覆う形となってしまい、他のViewが隠れてしまいデバッグできないという問題が発生しました。

現状では回避できず、iOS 18以下であればViewデバッグは可能なためそちらで確認するか、または、エンジニアの場合はXcodeのView Debuggerを使用して対処している状態です。

アクションシートの最適化

従来、UIAlertControllerではalertactionSheetの2つのスタイルで表現が変わっていましたが、iOS 26からはデフォルトではアクションシートも画面中央で表示されるようになります。

アクションシートの表示位置の比較

そのため、独自アクションシートと標準アクションシートをアプリ内で混在していると、iOS 26のユーザーは混乱してしまいます。 食べログでは一部だけあった独自アクションシートは全て標準アクションシートへ変更しました。

また、課題ではありませんが、今後の対応として、iOS 26からはiPadのように特定の場所から吹き出しのように表示させることができます。 対応箇所が多数になることから今回は見送っていますが、今後検討したいと考えています。

func showActionSheet(sender: UIView) {
    let actionSheet: UIAlertController
    // 初期化処理(省略)
    actionSheet.popoverPresentationController?.sourceItem = sender
    present(actionSheet, animated: true, completion: nil)
}

注意点として、iOS 18以下で上記コードを実行しても従来通り画面下部から表示されるアクションシートのままであり、あくまでiOS 26での動作のみが変更されます。

まとめ

Xcode 26/iOS 26対応を進めての所感

執筆時点ではまだリリースを迎えられてはいませんが、デザインが変わるという大きな変更に対して、以前のiOS 7でのフラットデザイン導入ほど混乱した状態にはならなかったという印象です。 一方で、ナビゲーションやタブバーは大きく変更されており、既存実装との整合性を保ちつつ対応するのは苦戦しました。

まだまだ課題は残されていますが、Xcode 26/iOS 26対応により、ユーザーへの価値提供だけでなく、開発者側にも最適な開発環境を提供していく意義は大きかったと感じています。

最後に

本記事では、食べログアプリで実際に発生したXcode 26/iOS 26対応の課題と解決策を紹介しました。 Xcode 26/iOS 26対応で困っているエンジニアの方々の参考になれば幸いです。 また、対応方法について質問やご意見があれば、ぜひコメントやSNSでお知らせください。

明日は @xi10on の「コードレビューの新たな役割:結果だけでなくプロセスの改善に取り組む」です。お楽しみに!


食べログでは、20年の歴史を未来へ繋いでいく仲間を募集しています!
ご興味のある方は、ぜひこちらもチェックしてみてください。