[ SwiftUI ]TextのModifierを使って特定のフォントリソースを簡単に利用できるようにする。その2


前回は動的に行間を調整できるModifierを書いてフォントリソースを簡単に利用できるためのコードを勉強したので、今回はディバイスことの表示位置調整とフォントサイズ調整を動的に行えるようなModifierを用意してフォントリソースを簡単に利用できるためのコードを勉強していこうと思います。

## ・準備

フォントのファイルやinfo.plistの準備などは前回の方法を使って準備していきます。

https://qiita.com/Apple81999/items/30852788eda8e5f5333d

## ・列挙型を使ってカスタムフォントの名前を管理する

ここまでは前回の方法と同じ方法を使います。

```swift
// 列挙型: カスタムフォントの名前を管理
enum CustomFontName {
    case DotGothic16
    // フォント名を返す関数
    func fontName() -> String {
        switch self {
        case .DotGothic16:
            return "DotGothic16-Regular" // フォント名を取得
            
        }
    }
}
```

## ・Modifierを用意する

geometryを使ってディバイスの画面サイズを取得して、設定した%に従ってサイズを動的に調整する。

```swift
struct FlexibleTextPositionModifier: ViewModifier {
    let horizontalPercent: CGFloat // nilの場合デフォルト位置を適用
    let verticalPercent: CGFloat // nilの場合デフォルト値を適用
    let widthPercent: CGFloat?
    let heightPercent: CGFloat?
    let fontSizePercent: CGFloat? // フォントサイズをビューの幅に基づいて変更
    let fontName: CustomFontName?// カスタムフォント名
    
    func body(content: Content) -> some View {
        GeometryReader { geometry in
            // フォントサイズを計算
            let calculatedFontSize = fontSizePercent.map { geometry.size.width * $0 } ?? 20 // デフォルト20
            
            content
                .font(fontName.map { Font.customFont(name: $0, size: calculatedFontSize) } ?? .system(size: calculatedFontSize))
                .frame(
                    width: widthPercent.map { geometry.size.width * $0 },
                    height: heightPercent.map { geometry.size.height * $0 }
                )
                .position(
                    x: geometry.size.width * horizontalPercent,
                    y: geometry.size.height * verticalPercent
                )
                .clipped()
        }
    }
}
```
View拡張で使いやすくする。

```swift
extension View {
    func flexibleTextPosition(
        horizontalPercent: CGFloat, // x
        verticalPercent: CGFloat, // y
        widthPercent: CGFloat? = nil, // Textの横幅を設定可能
        heightPercent: CGFloat? = nil, // Textの高さを設定可能
        fontSizePercent: CGFloat? = nil, // フォントサイズを設定可能
        fontName: CustomFontName? = nil // フォント名を任意で変更可能
    ) -> some View {
        self.modifier(
            FlexibleTextPositionModifier(
                horizontalPercent: horizontalPercent,
                verticalPercent: verticalPercent,
                widthPercent: widthPercent,
                heightPercent: heightPercent,
                fontSizePercent: fontSizePercent,
                fontName: fontName
            )
        )
    }
}
```
View に拡張を追加し、flexibleTextPosition メソッドを通じてこのモディファイアを簡単に適用できるようにする。
引数には、位置、サイズ、フォント設定を指定可能。
この方法でコードの再利用性を高め、より簡潔にビューにモディファイアを適用できます。

## ・説明
horizontalPercent と verticalPercent: ビュー内の水平・垂直位置を指定（0.0〜1.0)

widthPercentとheightPercent: 幅と高さをビューの全体サイズに基づいて設定するためのオプション

fontSizePercent: フォントサイズを親ビューの幅に基づいて調整
fontName: カスタムフォント名を指定するオプション

これらのプロパティを通じて、ビューの位置、サイズ、フォントスタイルを柔軟にカスタマイズできます。
```swift
    let horizontalPercent: CGFloat // nilの場合デフォルト位置を適用
    let verticalPercent: CGFloat // nilの場合デフォルト値を適用
    let widthPercent: CGFloat?
    let heightPercent: CGFloat?
    let fontSizePercent: CGFloat? // フォントサイズをビューの幅に基づいて変更
    let fontName: CustomFontName?// カスタムフォント名

```

フォントサイズを柔軟に変更しつつ、デフォルト値も用意して安全性を確保します。

```swift
let calculatedFontSize = fontSizePercent.map { geometry.size.width * $0 } ?? 20 // デフォルト20
```
contentにモディファイアを適用。
フォントを設定。fontName が指定されていればカスタムフォントを、なければシステムフォントを適用しフォントサイズは calculatedFontSize を利用する。

ビューに適用するフォントを柔軟に変更可能。
```swift
content
  .font(fontName.map { Font.customFont(name: $0, size: calculatedFontSize) } ?? .system(size: calculatedFontSize))
```
フレームサイズを親ビューの幅・高さに基づいて設定する（割合で計算)
widthPercent や heightPercent が未設定ならサイズ指定なしにする。
```swift
    .frame(width: widthPercent.map { geometry.size.width * $0 },
           height: heightPercent.map { geometry.size.height * $0 })
```
ビューの位置を親ビューのサイズと指定された割合を基に設定する。
水平方向と垂直方向の位置を柔軟に調整可能。
```swift
    .position(x: geometry.size.width * horizontalPercent,
              y: geometry.size.height * verticalPercent)
```
ビューの範囲外を切り取る。描画が範囲外に出ないようにする安全策。
```swift
    .clipped()
```

## ・コード全体

```swift
import SwiftUI
// 列挙型: カスタムフォントの名前を管理
enum CustomFontName {
    case DotGothic16
    // フォント名を返す関数
    func fontName() -> String {
        switch self {
        case .DotGothic16:
            return "DotGothic16-Regular" // R.swiftを利用してリソースからフォント名を取得
            
        }
    }
}

struct FlexibleTextPositionModifier: ViewModifier {
    let horizontalPercent: CGFloat // nilの場合デフォルト位置を適用
    let verticalPercent: CGFloat // nilの場合デフォルト値を適用
    let widthPercent: CGFloat?
    let heightPercent: CGFloat?
    let fontSizePercent: CGFloat? // フォントサイズをビューの幅に基づいて変更
    let fontName: CustomFontName?// カスタムフォント名
    
    func body(content: Content) -> some View {
        GeometryReader { geometry in
            // フォントサイズを計算
            let calculatedFontSize = fontSizePercent.map { geometry.size.width * $0 } ?? 20 // デフォルト20
            
            content
                .font(fontName.map { Font.customFont(name: $0, size: calculatedFontSize) } ?? .system(size: calculatedFontSize))
                .frame(
                    width: widthPercent.map { geometry.size.width * $0 },
                    height: heightPercent.map { geometry.size.height * $0 }
                )
                .position(
                    x: geometry.size.width * horizontalPercent,
                    y: geometry.size.height * verticalPercent
                )
                .clipped()
        }
    }
}

extension View {
    func flexibleTextPosition(
        horizontalPercent: CGFloat,
        verticalPercent: CGFloat,
        widthPercent: CGFloat? = nil,
        heightPercent: CGFloat? = nil,
        fontSizePercent: CGFloat? = nil, // フォントサイズを設定可能
        fontName: CustomFontName? = nil // フォント名を任意で変更可能
    ) -> some View {
        self.modifier(
            FlexibleTextPositionModifier(
                horizontalPercent: horizontalPercent,
                verticalPercent: verticalPercent,
                widthPercent: widthPercent,
                heightPercent: heightPercent,
                fontSizePercent: fontSizePercent,
                fontName: fontName
            )
        )
    }
}
```

## ・使い方

```swift
Text("Hello World")
.flexibleTextPosition(
 horizontalPercent: 0.5,
 verticalPercent: 0.4,
 fontSizePercent: 0.09,
 fontName: .DotGothic16
)

Text("Hello, World!")
    .flexibleTextPosition(
        horizontalPercent: 0.5,
        verticalPercent: 0.5,
        widthPercent: 0.8,
        heightPercent: nil,
        fontSizePercent: 0.05,
        fontName: .DotGothic16
    )
```
これでディバイスごとのX・Y位置とTextサイズ調整とフォントの種類を簡潔にまとめたModifierが用意できました。

## ・おわりに
swiftのstoryboardなどではディバイスごとのサイズ変化や位置調整の設定にいつもだいぶ手こずっていたので、この方法でmodifierを準備しておくことでディバイス対応がだいぶ楽になりました。