【SwiftUI】iOS 16.1でUINavigationBar.appearance().tintColorの設定が反映されなくなっていることに気付いて対応した

2022.11.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

iOS 16.1のシミュレータでアプリを触っていると、UINavigationBar.appearance().tintColorで設定しているはずのNavigationBarの戻るボタンの色がデフォルトの色になっていることに気付いたので対応しました。

環境

  • Xcode 14.1

コード

import SwiftUI

struct ContentView: View {

    init() {
        setupNavigationBarTintColor()
    }

    var body: some View {
        NavigationView {
            NavigationLink("Next") {
                NextView()
                    .navigationBarTitleDisplayMode(.inline)
            }
        }
    }

    private func setupNavigationBarTintColor() {
        // NavigationBarのtintColorを黒に変更
        UINavigationBar.appearance().tintColor = .black
    }
}

/// 次の画面
struct NextView: View {

    var body: some View {
        Rectangle()
            .fill(.mint)
            .overlay {
                Text("Next")
            }
    }
}

プレビュー

iOS 15.5

iOS 16.1

iOS 15.5では反映されていたUINavigationBar.appearance().tintColorが、iOS 16.1では反映されなくなっています。(今回検証で使用していたのはiOS 16.1ですが、iOS 16から同じ現象になっています)

対応策

その1. 色を変更したUIImageを設定

やや力技になりますが、色を変更したUIImageUINavigationBar.appearance().backIndicatorImageに設定して、タイトルの色の変更はUIBarButtonItem.appearance().setTitleTextAttributesを使用して変更する方法です。

上記、検証コードで紹介したsetupNavigationBarTintColorメソッドの内容を下記に変更します。

private func setupNavigationBarTintColor() {
    // 黒色のChevronLeftImageを作成
    let blackChevronLeftImage = UIImage(systemName: "chevron.backward")!.withTintColor(.black, renderingMode: .alwaysOriginal)

    // 黒に設定したUIImageをbackIndicatorImageに渡す
    UINavigationBar.appearance().backIndicatorImage = blackChevronLeftImage
    UINavigationBar.appearance().backIndicatorTransitionMaskImage = blackChevronLeftImage

    // UIBarButtonItemのタイトルテキストのforegroundColorを黒に設定
    UIBarButtonItem.appearance().setTitleTextAttributes([.foregroundColor: UIColor.black], for: .normal)
}

これでNavigationBarの戻るボタンの色をiOS 16.1でも意図していた色に変更することが出来ました。

その2. NavigationViewに.tintを設定

対策案1では、少し力技で変更したのですが、こちらはNavigationView.tintを使用するだけのシンプルな方法になります。

import SwiftUI

struct ContentView: View {

    var body: some View {

        NavigationView {
            NavigationLink("Next") {
                NextView()
                    .navigationBarTitleDisplayMode(.inline)
            }
        }
        .tint(.black)
    }
}

/// 次の画面
struct NextView: View {

    var body: some View {
        Rectangle()
            .fill(.mint)
            .overlay {
                Text("Next")
            }
    }
}

.tint(_:)を使用することで、Viewのデフォルトのアクセントカラーをオーバーライドすることが出来ます。.tint(_:)はiOS 15以上の利用となっていますが、現在非推奨となっているiOS 13.0以上で使用できるaccentColor(_:)でもアクセントカラーを設定でき、同じように意図した色の反映が確認出来ました。

おわりに

iOS 16から使用できるNavigationStackを使用する場合でも、同じようにUINavigationBar.appearance().tintColorの変更では戻るボタンの色には反映されず、.tint(_:)を使用して変更できることを確認しました。

まだまだ動きの定まらないSwiftUIのNavigationですが、広い心で受け止めてあげたいと思います。これからもよろしくお願いします。

この記事が誰かの助けになればと思います。

参考