いろは2986023 views
中学数学621382 views
小学社会308636 views
Computer365120 views
LaTeX957300 views
りんご192546 views
教育148875 views
小学理科717236 views
高校国語785655 views
MathPython491378 views
Help
Tools

English

SwiftUI 条件付きモディファイア

SwiftUI では条件に応じて異なるモディファイアを適用したい場面がある。しかし、実装方法によっては予期しない問題が発生することもある。条件付きモディファイアの正しいアプローチを理解しておこう。

三項演算子を使う方法(推奨)

最もシンプルで安全な方法は、三項演算子でプロパティの値を切り替えることだ。

Text("Hello")
    .foregroundColor(isHighlighted ? .red : .primary)
    .fontWeight(isImportant ? .bold : .regular)
    .padding(isExpanded ? 20 : 10)

この方法はビューの型が変わらないため、SwiftUI の差分検出が正しく機能し、アニメーションも期待通りに動作する。

if 文で分岐する方法(注意が必要)

if 文を使ってモディファイアを条件分岐させることもできるが、注意が必要だ。

// 問題のあるパターン
var body: some View {
    if isSpecial {
        Text("Hello")
            .foregroundColor(.red)
            .bold()
    } else {
        Text("Hello")
            .foregroundColor(.primary)
    }
}

この書き方では、isSpecial が変わるたびにビュー全体が再生成される。SwiftUI は 2 つの Text を同一のビューと認識しないため、アニメーションが途切れたり、状態がリセットされたりする可能性がある。

AnyView を避ける

型消去のために AnyView を使う方法は、パフォーマンス上の問題があり推奨されない。

// 避けるべきパターン
func styledText() -> AnyView {
    if isSpecial {
        return AnyView(Text("Hello").bold())
    } else {
        return AnyView(Text("Hello"))
    }
}

AnyView は SwiftUI の型システムを迂回するため、差分検出が非効率になる。

View extension で条件付きモディファイアを作る

条件付きでモディファイアを適用する extension を作る方法がある。

extension View {
    @ViewBuilder
    func `if`<Content: View>(
        _ condition: Bool,
        transform: (Self) -> Content
    ) -> some View {
        if condition {
            transform(self)
        } else {
            self
        }
    }
}
Text("Hello")
    .if(isHighlighted) { view in
        view.foregroundColor(.red).bold()
    }

ただし、この方法も内部で型が変わるため、三項演算子ほど安全ではない。単純なプロパティ変更には三項演算子を使い、複雑な条件分岐にのみこのパターンを使うのがよい。

オプショナル値への対応

オプショナルな値に基づいてモディファイアを適用する場合。

extension View {
    @ViewBuilder
    func ifLet<Value, Content: View>(
        _ value: Value?,
        transform: (Self, Value) -> Content
    ) -> some View {
        if let value = value {
            transform(self, value)
        } else {
            self
        }
    }
}
Text("商品名")
    .ifLet(discountRate) { view, rate in
        view.overlay(
            Text("-\(rate)%")
                .font(.caption)
                .foregroundColor(.red),
            alignment: .topTrailing
        )
    }

条件付き装飾の実践パターン

プロパティ値の切り替え

.foregroundColor(condition ? .red : .blue) のように三項演算子を使う。最も安全で効率的。

モディファイア自体の有無

ViewModifier を作成し、内部で条件分岐する。モディファイアの型は常に同じに保たれる。

複雑な分岐

@ViewBuilder を使った extension を検討。ただし、アニメーションへの影響を確認すること。

条件付き ViewModifier の例

struct ConditionalBorderModifier: ViewModifier {
    var showBorder: Bool
    var color: Color
    
    func body(content: Content) -> some View {
        content
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(showBorder ? color : Color.clear, lineWidth: 2)
            )
    }
}

extension View {
    func conditionalBorder(_ show: Bool, color: Color = .blue) -> some View {
        modifier(ConditionalBorderModifier(showBorder: show, color: color))
    }
}

モディファイア内で条件を処理することで、ビューの型を変えずに条件付きの装飾を実現できる。これが最も SwiftUI らしいアプローチであり、状態変化時のアニメーションも自然に動作する。