概要
デザイナーさん「このデザインで、トグルはこんな感じでお願いします」
ワイ「了解!disableの場合も設定して…出来たで!」
デザイナーさん「disableのときは灰色じゃなくて透明度を落とすだけにしてほしいです」
ワイ「…?」
この記事を読んで嬉しい人
SwiftUIを使ってiOS開発をしている人
トグルボタンをカスタムして自由に実装したい人
今回のお題
今回はトグルボタンのカスタムについてです。当初は普通にモディファイアを利用して実装をしていたのですが、disableの場合はデフォルトの灰色になってしまいました。ではなく薄い赤にする必要性がありました。
この場合の実装方法について少し悩んだので共有します。
実装
修正前
では問題の実装を見ていきましょう。
もともとの実装は以下です。
import SwiftUI
struct ToggleView: View {
@Binding var isToggleStatus: Bool
var body: some View {
VStack(spacing: 0) {
HStack {
Text("タイトル")
.font(.system(size: 14))
Toggle(isOn: $isToggleStatus) {}
.padding(.vertical)
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
.disabled(true)
}
}
}
}
#Preview {
ToggleView(
isToggleStatus: .constant(true)
)
}
ToggleStyleのtintはAcccentColorを指定していますが、これは自作のカラーで設定値は以下です。

この場合のプレビューがこちらになります。

この灰色になっているのを薄い赤にしたいわけです。
調べてみた
どうやら細かい設定をするにはToggleStyleプロトコルを適用したCustomToggleStyleを定義する必要がありそうです。iOS13以上で動作するのですが、最近のアプリの対応バージョン的にはそこまで気にする必要はなさそうです。
やる必要がありそうなことは以下
- CustomToggleStyle構造体内にmakeBody(configuration: Configuration)メソッドを実装
- ViewにToggleを配置
- toggleStyleモディファイアに自前で実装したCustomToggleStyleを指定
ではこれに従って実装を進めてみます。
修正後
まずは修正コード
import SwiftUI
struct CustomToggleStyle: ToggleStyle {
var onColor: Color
var offColor: Color
func makeBody(configuration: Configuration) -> some View {
HStack {
configuration.label
.font(.system(size: 14))
.foregroundStyle(.gray)
Spacer()
Rectangle()
.foregroundColor(configuration.isOn ? onColor : offColor)
.frame(width: 50, height: 30)
.overlay(
Circle()
.foregroundColor(.white)
.padding(2)
.offset(x: configuration.isOn ? 10 : -10, y: 0)
.animation(.easeInOut(duration: 0.2), value: configuration.isOn)
)
.onTapGesture {
configuration.isOn.toggle()
}
.cornerRadius(15)
}
}
}
struct ToggleView: View {
let title: String
@Binding var isToggleStatus: Bool
var isToggleDisabled: Bool
var body: some View {
let onColor = isToggleDisabled ? Color.accentSecondary : Color.accent
let offColor = Color.gray
Toggle(title, isOn: $isToggleStatus)
.toggleStyle(CustomToggleStyle(onColor: onColor, offColor: offColor))
.disabled(isToggleDisabled)
}
}
#Preview {
ToggleView(title: "タイトル", isToggleStatus: .constant(true), isToggleDisabled: true)
}
オン状態のときの色をdisableかどうかで指定しています。
accentSecondaryの色指定は以下です。

Opacityを50%に変更しただけです。
こちらのコードをpreviewした結果がこちらです。

指定通り、透明度の落ちた赤になっていることが確認いただけるかと思います!
要は何をしたの?
要はCustomToggleStyleでトグルのラベルとスタイルを指定したわけです。
大きく変わった点としては二点あります。
- タイトルとトグルボタンを並べていたのを、一つのスタイルにまとめた
- トグルボタンの形からアニメーションまで指定した
一つ目の違いについて、View内のトグルをよく見るとToggle(title, isOn: $isToggleStatus)
と書いています。つまり、タイトル自体をボタンに渡しているわけです。
これはCustomToggleStyleのHStack内を見るとSpacer()で間を開けてタイトルとトグルを左右に並べて配置していることがわかるかと思います。
また、Rectangle()で長方形を置いて、cornerRadiusモディファイアで楕円形にしています。
その上にoverlayで円を配置することでトグルボタンの見た目を再現しています。
ここを変更すると様々な形のトグルを作成することが可能なので、自分オリジナルのボタンを作ってみてください。
そして、onColorを楕円形の背景色に指定することで、自分が再現したい色で実装が可能になったというわけです。
まとめ
今回はCustomToggleStyleを利用して自分なりのトグルボタンを実装するという内容でした。
makeBody内で様々な実装が可能なので、より複雑な設定がボタンに必要な場合は是非利用を検討してください。
余談ですが
記事を書くうえでテンポラリープロジェクトにコードを書いていたところ
import SwiftUI
struct ToggleView: View {
@Binding var isToggleStatus: Bool
var body: some View {
VStack(spacing: 0) {
HStack {
Text("タイトル")
.font(.system(size: 14))
Toggle(isOn: $isToggleStatus) {}
.padding(.vertical)
.toggleStyle(SwitchToggleStyle(tint: .red))
.disabled(true)
}
}
}
}
#Preview {
ToggleView(
isToggleStatus: .constant(true)
)
}
こちらの実装ではきちんと薄い赤になってしまいました。

色指定に.red
を利用しただけなんですが、今回はデザイナーさん指定のカスタムカラーを利用したため、今回のような煩雑な処理が必要になってしまったようです。
もしかするとColorの設定をうまくすればよいのかもしれなかったですが、今回はCustomToggleStyleの利用に関する記事なので一応これでもうまくいったよの共有までにしておきます(悔しい)。