Styling with Liquid Glass Availability

Tags: [Swift, Liquid Glass, Blog]


この記事はCYBOZU SUMMER BLOG FES ’25の記事です。


iOS26ではButtonTextFieldがLiquid Glassに対応するコンポーネントになりましたが、2025/10/03現在ではOSバージョンによる分岐が必要です。

しかしこの分岐はlayoutとstylingの問題で、SwiftUIコード上のセマンティックな部分は共通のため、View全体を分岐するのは避けたいところです。

この要求を満たすため、stylingの問題はView styleコードの中に押し込めると、既存のコードとも適合します。

TextFieldStyle

TextFieldStyleconfigurationがそのままTextFieldになっているため、シンプルです。

struct MyGlassTextFieldStyle: TextFieldStyle {
    func _body(configuration: TextField) -> some View {
        if #available(iOS 26.0, *) {
            configuration
                .padding(8)
                .glassEffect()
        } else {
            configuration
                .textFieldStyle(.roundedBorder)
        }
    }
}

ButtonStyle

ButtonStyleTextFieldStyleより少し複雑です。

既存のボタンの多くはbuilt-inもしくはユーザー定義のButtonStyleが活用されていると思いますが、configurationButtonStyleConfigurationのため、TextFieldStyleのようなstyleの中では適用されません。

@available(iOS 26.0, *)
struct MyGlassButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
            .padding(8)
            .glassEffect()
    }
}

MyGlassButtonStyleは既存のButtonStyleとはconcreteな型が異なるため、.buttonStyle()内で呼び分けることはできません。ViewModifierで分岐します。

struct MyGlassButton: ViewModifier {
    var fallBack: FallBack
    func body(content: Content) -> some View {
        if #available(iOS 26.0, *) {
            content
                .buttonStyle(MyGlassButtonStyle())
        } else {
            content
                .buttonStyle(.myButtonStyle)
        }
    }
}

extension View {
    func myGlassButton() -> some View {
        self.modifier(MyGlassButton())
    }
}

これをButtonに適用します。

struct MyButton: View {
    var body: some View {
        Button("Tap me") { print("hello") }
            .myGlassButton()
    }
}

これらの対応でセマンティックな共通部分を持ちつつ、コンポーネントのスタイルを分岐できました。

OSバージョンによる分岐も既存のView Styles APIの形式を踏襲するとSwiftUIの世界に適合しやすい形で記述できます。


© 2025 La voix du code

Created in Swift with Ignite