LoginSignup
0
0

SwiftUI トグル式 角丸ボタン

Posted at

はじめに

iOSデバイスでPCコントローラーを作る《こちらの記事》で使っている、トグル式の角丸ボタンの紹介です。

『swiftui 角丸ボタン』でググると、角丸ボタンの作り方がいくつもヒットしますが、今回作成するボタンは『トグル式』です。

『トグル式』とは

下記のGIFアニメの通り、ボタンを押すと凹んだ状態を維持し、もう一度押すと元に戻るボタンのことです。(「プッシュプル式」が正しい?)

toggle_button.gif

「凹んだ状態」を制御

角丸ボタンに「凹んだ状態」であることを意味するプロパティisSelectedを追加して、ボタンの背景色と文字色を入れ替えるだけです。これに加えて、このプロパティを親Viewから変更することで、ボタンを押す操作以外でも状態を変えることが必要でした。

角丸にして背景を塗る

基本形は次のコードとなります。

struct ContentView: View {
    let foreColor = Color.accentColor
    let backgroundColor = Color.white
    @State var isSelected: Bool = false
    var body: some View {
        VStack {
// ↓↓↓
            Button(action: {
                print("Button Pushed!")
            }) {
                Text("Button")
                    .bold()
                    .padding()
                    .frame(width: 100, height: 50)
                    .foregroundColor(isSelected ? backgroundColor: foreColor)
                    .overlay(
                        RoundedRectangle(cornerRadius: 10)
                            .stroke(Color.black, lineWidth: 3)
                    )
                    .background(isSelected ? foreColor : backgroundColor)
            }
// ↑↑↑
            Button("toggle") {
                isSelected.toggle()
            }
        }
    }
}

Buttonボタンの下のtoggleボタンを押すと、角丸ボタンの外観を変更します。

b1.png b2.png

角丸ボタンのアクション内でisSelected.toggle()を実行すれば、『ボタンを押すと凹んだ状態を維持し、もう一度押すと元に戻るボタン』の完成。

次に、このボタンを汎用化(関数化)します。

RoundedRectangleSelectableButton

⭐️印で示したisSelectedtrueの場合に凹んだ状態を示し、falseの場合に、元に戻します。

RoundedRectangleSelectableButton.swift
struct RoundedRectangleSelectableButton: View {
    let text: String
    let textColor: Color
    let backgroundColor: Color
    let width: CGFloat
    let height: CGFloat
    let cornerRadius: CGFloat
    let borderColor: Color
    let borderWidth: CGFloat
    let action: (_ selected: Bool)->Void
    private var autoSelect: Bool? = nil
    @Binding private var isSelected: Bool            //⭐️
    init(_ text: String, textColor: Color = Color(uiColor: .tintColor), backgroundColor: Color = Color(uiColor: .systemBackground), width: CGFloat, height: CGFloat, cornerRadius: CGFloat = 8, borderColor: Color = Color(uiColor: .label), borderWidth: CGFloat = 2, isSelect: Binding<Bool>, action: @escaping (_ selected: Bool)->Void) {
        self.text = text
        self.textColor = textColor
        self.backgroundColor = backgroundColor
        self.width = width - 4
        self.height = height
        self.cornerRadius = cornerRadius
        self.borderColor = borderColor
        self.borderWidth = borderWidth
        self.action = action
        _isSelected = isSelect
    }
    var body: some View {
        Button(action: {
            if let autoSelect = self.autoSelect, autoSelect {
                isSelected.toggle()
            }
            action(isSelected)
        },label:  {
            Text(text)
                .foregroundColor(isSelected ? Color.white : textColor)
                .frame(width: width, height: height)
                .background(isSelected ? Color(uiColor: .tintColor) : backgroundColor)
                .cornerRadius(cornerRadius)
                .overlay(
                    RoundedRectangle(cornerRadius: cornerRadius)
                        .stroke(borderColor, lineWidth: borderWidth)
                )
        })
    }
    func autoSelect(_ autoSelect: Bool) -> Self {
        var view = self
        view.autoSelect = autoSelect
        return view
    }
}

引数の説明

引数 意味 省略値
text ボタンのラベル String -(省略不可)
textColor ラベルの色 Color UIColor.tintColor
backgroundColor 背景色 Color UIColor.systemBackground
width CGFloat -(省略不可)
height 高さ CGFloat -(省略不可)
cornerRadius 角丸のサイズ CGFloat 8
borderColor 枠線の色 Color UIColor.label
borderWidth 枠線の太さ CGFloat 2
isSelect 凹み状態の変数 Binding<Bool> -(省略不可)
action ボタンが押された時に実行するアクション (_ selected: Bool)->Void -(省略不可)

色の省略値にシステム色を使用することで、ダークモード時もいい感じにしてくれます。

オプション(View Modifier)

名称 引数の型 意味 オプション
省略値
autoSelect Bool 自動でトグル切り替えするかどうか。
自動にする:true、しない:false
false

使い方

次のコードで、3つの使い方を説明します。

struct ContentView: View {
    let foreColor = Color.accentColor
    let backgroundColor = Color.white
    @State var isSelect1: Bool = false
    @State var isSelect2: Bool = false
    @State var isSelect3: Bool = false
    @State var isSelect4: Bool = false
    var body: some View {
        VStack {
            VStack {
                Text("パターン1")
                RoundedRectangleSelectableButton("Normal", width: 100, height: 40, isSelect: $isSelect1, action: { _ in
                    //action: todo
                })
                Button("Toggle") {
                    isSelect1.toggle()
                }
            }
            .padding(.bottom, 50)
            VStack {
                Text("パターン2")
                RoundedRectangleSelectableButton("Toggle", width: 100, height: 40, isSelect: $isSelect2, action: { _ in
                    //action: todo
                })
                .autoSelect(true)
            }
            .padding(.bottom, 50)
            VStack {
                Text("パターン3")
                HStack {
                    RoundedRectangleSelectableButton("Option A", width: 100, height: 40, isSelect: $isSelect3, action: { _ in
                        isSelect3.toggle()
                        if isSelect3 { isSelect4 = false }
                        //action: todo
                    })
                    Text("or")
                    RoundedRectangleSelectableButton("Option B", width: 100, height: 40, isSelect: $isSelect4, action: { _ in
                        isSelect4.toggle()
                        if isSelect4 { isSelect3 = false }
                        //action: todo
                    })
                }
            }

        }
    }
}

light.png dark.png

パターン1

角丸の普通のボタン。引数isSelectの設定で親ビューから外見を変更可能です。
Toggleをタップすると外観が変化します。

パターン2

角丸の普通のトグル式ボタン。.autoSelect(true)を指定することで、トグル切り替えを自動化しています。

パターン3

「オプションを選択する」とかで使用するボタンです。「Option A」か「Option B」か両方無しの組み合わせだけ許可します。.autoSelectは使わずに、引数isSelectで指定した変数を制御することで実現しています(グループ化の機能があれば、もっと楽できますね)。

toggle_button_full.gif

おわりに

グループ化の機能は、今後追加しようと思います。
よかったら使ってみてください。

以上です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0