【SwiftUI】toggleStyleを使ってToggleをカスタマイズ

SwiftUI

前回に引き続きSwiftUIでのToggleの使い方について、特に今回はtoggleStyleを使用してトグルボタンの見た目をカスタマイズする方法について詳しく説明していきます。これをマスターすれば、チェックボックス形式のトグルも実装できます。

環境

  • Xcode13.0
  • 動作はシミュレーターのiPhone13 miniで確認

前回のおさらい

よくあるToggleは以下のように実装でき、foregroundColorでテキストの色変更、toggleStyle(SwitchToggleStyle(tint: )) でボタンの色変更ができます。

struct ContentView: View {
    @State private var isOn = true
    var body: some View {
        Toggle("トグル", isOn: $isOn)
            .foregroundColor(.red)
            .toggleStyle(SwitchToggleStyle(tint: .orange))
    }
}

ToggleStyleを継承した構造体の用意

まずはトグルボタンをカスタマイズするための構造体を用意します。これはToggleStyleを継承しておきましょう。今回は MyToggleStyle という名称にしました。
これをトグルボタンに適用するには、 .toggleStyle(MyToggleStyle()) というようにモディファイアを使用します。

struct MyToggleStyle: ToggleStyle {

    func makeBody(configuration: Configuration) -> some View {
        // 好みのトグルのデザイン実装する
    }

}

struct ContentView: View {
    @State private var isOn = true
    var body: some View {
        Toggle("トグル", isOn: $isOn)
            .toggleStyle(MyToggleStyle())
    }
}

ベーシックなトグルのスタイル変更

トグルをカスタマイズするにはToggleStyleを継承した構造体の func makeBody(configuration: Configuration) の中で好みのデザインを実装する必要があります。

この時、configurationには .label と .isOn というプロパティがあり、これを使用して自由にカスタマイズができます。

では簡単なものを作ってみましょう。

struct MyToggleStyle: ToggleStyle {

    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.label
            Image(systemName: configuration.isOn ? "checkmark.square" : "square")
                .resizable()
                .frame(width: 20, height: 20)
                .onTapGesture {
                    configuration.isOn.toggle()
                }
        }
    }
}

トグルのオンオフに合わせてチェックボックスにチェックが入るトグルを作ってみました。
たったこれだけのコードでこんな感じに

アニメーションするトグル

元々のトグルのデザインのように、タップされたらアニメーションさせることもできます。

struct MyToggleStyle: ToggleStyle {

    func makeBody(configuration: Configuration) -> some View {
        HStack {
            configuration.label
            RoundedRectangle(cornerRadius: 12.0)
                .frame(width: 42, height: 24, alignment: .center)
                .foregroundColor(.gray)
                .overlay(
                    Circle()
                        .padding(3)
                        .foregroundColor(.white)
                        .offset(x: configuration.isOn ? 8 : -8, y: 0)
                        .animation(.linear)
                )
                .onTapGesture(perform: {
                    configuration.isOn.toggle()
                })

        }
    }
}

コメント