英語607877 views
ヒストリア284143 views
いろは2986023 views
MathPython491378 views
雑学1472593 views
高校化学2913383 views
高校物理158224 views
世界の国560595 views
教育148875 views
高校倫理1433119 views
Help
Tools

English

SwiftUI ViewModifier プロトコル

ViewModifier プロトコルは、ビューに対する変更をカプセル化して再利用可能にする仕組みだ。複数のモディファイアをまとめたり、条件に応じた装飾を定義したりする際に活用する。

ViewModifier の基本構造

ViewModifier プロトコルは body(content:) メソッドを要求する。content は元のビューを表し、それに変更を加えて返す。

struct CardStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .background(.white)
            .cornerRadius(12)
            .shadow(radius: 4)
    }
}

この CardStyle を適用すると、パディング、白背景、角丸、影がまとめて適用される。

modifier() で適用

ViewModifier は .modifier() を使って適用する。

Text("カード内のテキスト")
    .modifier(CardStyle())

ただし、毎回 .modifier() と書くのは冗長なので、View の extension を用意するのが一般的だ。

extension View {
    func cardStyle() -> some View {
        modifier(CardStyle())
    }
}

// 使用
Text("カード内のテキスト")
    .cardStyle()

パラメータを持つ ViewModifier

ViewModifier にプロパティを持たせて、動的な設定を可能にできる。

struct RoundedBorder: ViewModifier {
    var color: Color
    var width: CGFloat
    var cornerRadius: CGFloat
    
    func body(content: Content) -> some View {
        content
            .overlay(
                RoundedRectangle(cornerRadius: cornerRadius)
                    .stroke(color, lineWidth: width)
            )
    }
}

extension View {
    func roundedBorder(
        color: Color = .gray,
        width: CGFloat = 1,
        cornerRadius: CGFloat = 8
    ) -> some View {
        modifier(RoundedBorder(
            color: color,
            width: width,
            cornerRadius: cornerRadius
        ))
    }
}

デフォルト値を設定しておけば、呼び出し側で必要なパラメータだけ指定できる。

Text("デフォルト")
    .roundedBorder()

Text("カスタム")
    .roundedBorder(color: .blue, width: 2, cornerRadius: 16)

環境変数を使った ViewModifier

ViewModifier 内で @Environment を使って、環境に応じた動作を実装できる。

struct AdaptiveStyle: ViewModifier {
    @Environment(\.colorScheme) var colorScheme
    
    func body(content: Content) -> some View {
        content
            .padding()
            .background(colorScheme == .dark ? Color.gray : Color.white)
            .cornerRadius(8)
    }
}

ダークモードとライトモードで自動的に背景色が切り替わる。

条件付きの ViewModifier

ViewModifier 内で条件分岐を行い、状態に応じた装飾を適用できる。

struct HighlightModifier: ViewModifier {
    var isHighlighted: Bool
    
    func body(content: Content) -> some View {
        content
            .padding(isHighlighted ? 16 : 8)
            .background(isHighlighted ? Color.yellow.opacity(0.3) : Color.clear)
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(isHighlighted ? Color.yellow : Color.clear, lineWidth: 2)
            )
    }
}

extension View {
    func highlight(_ isHighlighted: Bool) -> some View {
        modifier(HighlightModifier(isHighlighted: isHighlighted))
    }
}
Text("重要な項目")
    .highlight(isImportant)

concat で ViewModifier を連結

複数の ViewModifier を concat で連結できる。

struct CombinedModifier: ViewModifier {
    func body(content: Content) -> some View {
        content.modifier(CardStyle().concat(RoundedBorder(
            color: .blue,
            width: 1,
            cornerRadius: 12
        )))
    }
}

ただし、実際には extension で複数のモディファイアを順に呼ぶ方が可読性は高い。

いつ ViewModifier を使うか

ViewModifier が適している

複数のモディファイアをまとめたい。アプリ全体で統一したスタイルを定義したい。パラメータ化して再利用したい。

直接モディファイアを書く方がいい

1〜2 個のモディファイアを適用するだけ。特定の場所でしか使わない一度きりの装飾。

ViewModifier はデザインシステムの構築に欠かせない機能だ。カードスタイル、ボタンスタイル、入力フィールドスタイルなどをモディファイアとして定義しておけば、アプリ全体で一貫した見た目を維持しやすくなる。