web-dev-qa-db-ja.com

SwiftUIのカスタムフォントでダイナミックタイプサイズを使用することは可能ですか?

私はSwiftUIをいじっており、プロジェクトにカスタムUIフォントを使用したいと考えています。ただし、組み込みのフォントクラスに付属している動的な型のサイズ変更(大きなタイトルなど)を失いたくありません。

AppleはTextのカスタムフォント修飾子を提供しています:

_Text("Hello, world!")
    .font(.custom("Papyrus", size: 17))
_

ただし、これによりサイズが17ptに固定されます。これをデバイスまたはシミュレーターで実行し、アクセシビリティインスペクターを開いてOSレベルのフォントサイズを調整しても、Text要素は更新されません。

_size:_パラメータはオプションではないため、何かを渡す必要があります。残念ながら、sizeにはFontパラメータがないため、既存のフォントのsizeを取得することはできません(カスタムフォントであっても)。

残りのSwiftUIの一般的なパターンのように、パラメーターはオプションにすることも、nilを渡して特定の動作を明示的に無効にすることもできます。 .custom()の_size:_パラメータはオプションであり、内部では以前のFont修飾子のサイズを使用するか、Text

または、システムスタイルを定義する静的メソッド(_.largeTitle_など)は、カスタムフォント名を提供する引数を受け入れることができます:.largeTitle("Papyrus")

誰か回避策はありますか?

10
mbxDev

ViewModifierでもこれを実現するための良い方法を見つけました。動的タイプとカスタムフォントについて、ベース修飾子を このHacking With Swiftの記事 から借りました。結果は次のとおりです。

import SwiftUI

@available(iOS 13, macCatalyst 13, tvOS 13, watchOS 6, *)
struct CustomFont: ViewModifier {
    @Environment(\.sizeCategory) var sizeCategory

    var name: String
    var style: UIFont.TextStyle
    var weight: Font.Weight = .regular

    func body(content: Content) -> some View {
        return content.font(Font.custom(
            name,
            size: UIFont.preferredFont(forTextStyle: style).pointSize)
            .weight(weight))
    }
}

@available(iOS 13, macCatalyst 13, tvOS 13, watchOS 6, *)
extension View {
    func customFont(
        name: String,
        style: UIFont.TextStyle,
        weight: Font.Weight = .regular) -> some View {
        return self.modifier(CustomFont(name: name, style: style, weight: weight))
    }
}

そして使い方:

Text("Hello World!")
    .customFont(name: "Georgia", style: .headline, weight: .bold)

このようにして、明示的にサイズを指定する必要なく、バンドルされたテキストスタイルに固執できます。そうしたい場合は、font修飾子を使用してすでに許可されており、スケーリングは、この質問に与えられた代替アプローチの1つを使用して処理できます。

また、スタイルはViewModifier準拠struct内に適用され、環境のsizeCategoryへの変更に応答するため、ビューにはアクセシビリティへの変更が反映されることに注意してください。アプリに切り替えた直後の設定。したがって、再起動する必要はありません。

4
Xtian D.

私が行う方法は、環境のサイズカテゴリの変更にバインドできるカスタム修飾子を作成することです。

Papyrusを使用する必要があるときはいつでも、次のように使用します。

Text("Hello World!").modifier(Papyrus())

またはこのように:

Text("Hello World!").modifier(Papyrus(.caption))
Text("Hello World!").modifier(Papyrus(.footnote))
Text("Hello World!").modifier(Papyrus(.subheadline))
Text("Hello World!").modifier(Papyrus(.callout))
Text("Hello World!").modifier(Papyrus())
Text("Hello World!").modifier(Papyrus(.body))
Text("Hello World!").modifier(Papyrus(.headline))
Text("Hello World!").modifier(Papyrus(.title))
Text("Hello World!").modifier(Papyrus(.largeTitle))

これでテキストは動的に変化し、追加の作業は必要ありません。これは同じコードで、異なるテキストサイズ設定に反応します。

enter image description here

そして、Papyrus()の実装は次のようになります。各カテゴリの適切な値を把握する必要があります。これは単なる例です。

struct Papyrus: ViewModifier {
    @Environment(\.sizeCategory) var sizeCategory
    var textStyle: Font.TextStyle

    init(_ textStyle: Font.TextStyle = .body) {
        self.textStyle = textStyle
    }

    func body(content: Content) -> some View {
        content.font(getFont())
    }

    func getFont() -> Font {
        switch(sizeCategory) {
        case .extraSmall:
            return Font.custom("Papyrus", size: 16 * getStyleFactor())
        case .small:
            return Font.custom("Papyrus", size: 21 * getStyleFactor())
        case .medium:
            return Font.custom("Papyrus", size: 24 * getStyleFactor())
        case .large:
            return Font.custom("Papyrus", size: 28 * getStyleFactor())
        case .extraLarge:
            return Font.custom("Papyrus", size: 32 * getStyleFactor())
        case .extraExtraLarge:
            return Font.custom("Papyrus", size: 36 * getStyleFactor())
        case .extraExtraExtraLarge:
            return Font.custom("Papyrus", size: 40 * getStyleFactor())
        case .accessibilityMedium:
            return Font.custom("Papyrus", size: 48 * getStyleFactor())
        case .accessibilityLarge:
            return Font.custom("Papyrus", size: 52 * getStyleFactor())
        case .accessibilityExtraLarge:
            return Font.custom("Papyrus", size: 60 * getStyleFactor())
        case .accessibilityExtraExtraLarge:
            return Font.custom("Papyrus", size: 66 * getStyleFactor())
        case .accessibilityExtraExtraExtraLarge:
            return Font.custom("Papyrus", size: 72 * getStyleFactor())
        @unknown default:
            return Font.custom("Papyrus", size: 36 * getStyleFactor())
        }
    }

    func getStyleFactor() -> CGFloat {
        switch textStyle {
        case .caption:
            return 0.6
        case .footnote:
            return 0.7
        case .subheadline:
            return 0.8
        case .callout:
            return 0.9
        case .body:
            return 1.0
        case .headline:
            return 1.2
        case .title:
            return 1.5
        case .largeTitle:
            return 2.0
        @unknown default:
            return 1.0
        }
    }

}

[〜#〜]更新[〜#〜]

テキストスタイルをパラメーターとして受け入れるように実装を変更しました。

13
kontiki

SwiftUIのようなスタイルを維持したい場合は、UIKit互換プラットフォーム用にFontを拡張できます。

_import SwiftUI

extension Font {

    #if canImport(UIKit)

    static var myHeadline = Font.custom(
        "Your-Font-Name",
        size: UIFontMetrics(forTextStyle: .headline).scaledValue(for: 17)
    )

    #endif
}
_

次に、それを使用するには:

_Text("Hello World!")
    .font(.myHeadline)
_

アプリケーションを再起動しない限り、カスタムフォントは更新されないことに注意してください。つまり、キャンバスプレビューはこの方法でも機能しません。

時間があれば、このトピックをさらに調査します。

(また、これはネイティブ機能である必要があります。Font.system(_ style: Font.TextStyle, design: Font.Design = .default)がある場合は、Font.custom(_ name: String, style: Font.TextStyle)も必要です。フィードバックのFB6523689を参照してください。)

1
Alex