3
1
iOS強化月間 - iOSアプリ開発の知見を共有しよう -

非日本語圏日本語フォント問題、SwiftUIにおける対処事例(2023年中旬)

Last updated at Posted at 2023-09-26

デバイスの言語設定で日本語を選択していないような場面において、日本語文章のフォントが想定外のものになる現象は多くのデバイスで発生する。

例.png

今回は2023年中旬にAppleプラットフォーム向けアプリに実施したSwiftUIにおける対処事例を紹介する。

参考

2015年に公開された下記のブログポストを参考にした。このポストでは「本現象の解説」と「UIKit環境におけるNSAttributedStringを用いた解決策」を示している。また、本現象のことを「中華フォント現象」と呼んでいる。

対象アプリ

アプリ.png

  • 全てSwiftUIで実装
  • iOS 15以降をサポート
  • iOS/iPadOS/watchOS/macOS/tvOSといった全てのAppleプラットフォームでリリース
  • 英語(プライマリー)と日本語に対応
  • 将棋の駒をシンプルにSwiftUI.Textで表示
  • OSの言語設定で日本語を指定していない場合、将棋の駒に対して本現象が発生
  • 特に「角」は見た目が大きく変化

将棋盤.png

角.png

2023年5月にリリースしたバージョン1.4にて本現象を解消した。

対処内容

前記のブログポストを参考にしつつ、今回はNSAttributedStringの後継API(?)であるAttributedStringを採用した。

AttributedString | Apple Developer Documentation

最低限の実装例

struct 例1: View {
    var 文字列: String
    private var attributedContent: AttributedString {
        var  = AttributedString(stringLiteral: self.文字列)
        .languageIdentifier = "ja" // 👈ここで指定する
        return 
    }
    var body: some View {
        Text(self.attributedContent)
    }
}

実際のアプリとほぼ同様の実装例

「Plain将棋盤」では駒の文字のサイズ変えたり太字にしたりセリフ体にしたりアンダーライン引いたりする必要があるため、Textに対する各装飾をSwiftUIのModifierではなくAttributedStringレイヤーで全て適用することにした。

struct 例2: View {
    var 文字列: String
    var サイズ: CGFloat
    var 太字: Bool
    var セリフ体: Bool
    var アンダーライン: Bool
    private var attributedContent: AttributedString {
        var  = AttributedString(stringLiteral: self.文字列)
        .font = Font.system(size: self.サイズ,
                             weight: self.太字 ? .bold : .regular,
                             design: self.セリフ体 ? .serif : .default)
        if self.アンダーライン {
            .underlineStyle = .single
        }
        .languageIdentifier = "ja"
        return 
    }
    var body: some View {
        Text(self.attributedContent)
    }
}

iOS 17世代以降の新API

iOS 17世代(2023年秋)以降のAppleプラットフォーム向けにtypesettingLanguageというViewModifierが登場した。

In some cases Text may contain text of a particular language which doesn’t match the device UI language. In that case it’s useful to specify a language so line height, line breaking and spacing will respect the script used for that language.

struct 例3: View {
    var 文字列: String
    var body: some View {
        Text(self.文字列)
            .typesettingLanguage(.init(languageCode: .japanese))
    }
}

たった1行書くだけで解決出来るようになったっぽい。


Text(self.文字列)
    .typesettingLanguage(.init(identifier: "ja"))
Text(self.文字列)
    .typesettingLanguage(.init(languageCode: .japanese))
Text(self.文字列)
    .typesettingLanguage(.init(script: .japanese))
Text(self.文字列)
    .typesettingLanguage(.init(region: .japan))
Text(self.文字列)
    .typesettingLanguage(.explicit(.init(languageCode: .japanese)))

いくつかのパラメータが用意されている。上記の各コードで(恐らく)同様の表示がされるようだ。私個人の理解力ではその差は不明。


スクリーンショット 2023-09-27 13.05.46.png

UIKit向けにも同等のAPIがあるようだ。

まとめ

  • iOS 17世代以降の環境ならtypesettingLanguage
  • iOS 15世代以降、iOS 17世代より前の環境ならAttributedString
  • 古い環境も対象になるUIKit等ならNSAttributedString

を採用すれば良さそう。

リンク

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1