SwiftUIでトグルのカスタムスタイルを適用

SwiftUIでは、`ToggleStyle`を使用してトグル（スイッチ）の外見をデザインすることができます。トグルの色、形、アニメーションの変更を行うことが可能です。

<img width="600" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/a201faf0-3d61-6288-c6f5-fb078ec40bd2.png">

## `ToggleStyle` の構造体

始めるにあたり、先ず `ToggleStyle` タイプに対応する構造体を実装する必要があります：

```swift
struct NewToggleStyle: ToggleStyle {
    
    func makeBody(configuration: Configuration) -> some View {
        // トグルのUIコンポーネント
    }
    
}
```

この形式を適用するには、このコードを利用してください：

```swift
@State var isSwitchOn = false
var body: some View {
    Toggle("Toggle", isOn: $isSwitchOn)
        .toggleStyle(NewToggleStyle())
}
```

## `configuration` 変数

この `configuration` 変数を利用すると、そのトグルのプロパティにアクセスすることができます

| 変数名  | 用法  |
|---|---|
| configuration.label  | トグルの文字列ラベル（名前）  |
| configuration.isOn  | トグルのオン/オフを表示するブール型変数  |

ユーザーがトグル（スイッチ）をタップする際、コード内の `configuration.isOn` を更新する必要があることに注意してください。

## 基本的なトグルのスタイル

まず、元のトグルのスタイルをデザインすることで開始できます

<img width="300" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/4c6c7bd6-568e-184a-8937-2de12d11d131.gif">

```swift
struct NewToggleStyle: ToggleStyle {
    
    func makeBody(configuration: Configuration) -> some View {
        
        HStack {
            
            configuration.label
            
            RoundedRectangle(cornerRadius: 25.0)
                .frame(width: 50, height: 30, alignment: .center)
                .overlay((
                    Circle()
                        .foregroundColor(Color(.systemBackground))
                        .padding(3)
                        .offset(x: configuration.isOn ? 10 : -10, y: 0)
                        .animation(.linear)
                ))
                .foregroundColor(Color(.label))
                .onTapGesture(perform: {
                    configuration.isOn.toggle()
                })
            
        }
        
    }
    
}
```

上記のコード例では、

1) `configuration.isOn` を使用して、円の位置のオフセットを計算する必要があります。

```swift
.offset(x: configuration.isOn ? 10 : -10, y: 0)
```

2) `onTapGesture` を使用して `configuration.isOn` を設定する必要があります

```swift
.onTapGesture(perform: {
    configuration.isOn.toggle()
})
```

## SF Symbolの画像をトグル

トグルスイッチがオンまたはオフになっているときにSF Symbolの画像を指定できます。

<img width="300" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/23df13ac-6ee3-a4c6-9bbc-5f3e09ca9823.gif">

```swift
struct NewToggleStyle: ToggleStyle {
    
    static let backgroundColor = Color(.label)
    static let switchColor = Color(.systemBackground)
    
    func makeBody(configuration: Configuration) -> some View {
        
        HStack {
            
            configuration.label
            
            RoundedRectangle(cornerRadius: 25.0)
                .frame(width: 50, height: 30, alignment: .center)
                .overlay((
                    Image(systemName: configuration.isOn ? "checkmark.circle.fill" : "xmark.circle.fill")
                        .font(.system(size: 20))
                        .foregroundColor(configuration.isOn ? .white : NewToggleStyle.switchColor)
                        .padding(3)
                        .offset(x: configuration.isOn ? 10 : -10, y: 0)
                        .animation(.linear)
                ))
                .foregroundColor(configuration.isOn ? .green : NewToggleStyle.backgroundColor)
                .onTapGesture(perform: {
                    configuration.isOn.toggle()
                })
            
        }
        
    }
    
}
```

## さらにトグル

自分のデザインアイデアを使用して、自分だけのトグルをデザインすることができるようになりました。この懐中電灯のように:

<img width="300" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635330/8c2251fd-fc6a-8d8f-21a7-e9d68ac57adc.gif">

```swift
struct NewToggleStyle: ToggleStyle {
    
    static let backgroundColor = Color(.label)
    static let switchColor = Color(.systemBackground)
    
    func makeBody(configuration: Configuration) -> some View {
        
        VStack {
            
            ZStack {
                Image(systemName: "arrowtriangle.down.fill")
                    .frame(width: 30, height: 30, alignment: .top)
                    .font(.system(size: 50))
                    .offset(y: -30)
                    .foregroundColor(.yellow)
                    .opacity(configuration.isOn ? 1 : 0)
                    .animation(.easeInOut)
                Image(systemName: configuration.isOn ? "flashlight.on.fill": "flashlight.off.fill")
                    .font(.system(size: 50))
                    .opacity(configuration.isOn ? 1 : 0.7)
                    .animation(.default)
            }
            .onTapGesture(perform: {
                configuration.isOn.toggle()
            })
            
            configuration.label
            
        }
        
    }
    
}
```

[https://github.com/mszmagic/SwiftUI-ToggleStyle](https://github.com/mszmagic/SwiftUI-ToggleStyle)

---

:relaxed: [Twitter @MszPro](https://twitter.com/MszPro)

:sunny: [私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。](http://mszpro.com/ioskiji/)
<img width="150" src="https://cdn.mszmagic.com/static-web-content/AppClipImage_small.png">