Sponsor Link
環境&対象
- macOS Big Sur 11.3
- Xcode 12.5
- iOS 14.5
Stateful な Button
状態を持たせたいので、内部に Bool を持つような Button とします。
内部の Bool の状態に応じて表示する Label を切り替えるようにします。
また、内部の Bool の状態を取得したい時があるので、ボタンが押された時に、(変更後の Bool を引数に) 指定 closure が呼ばれるように作ります。
要件
つまり、以下が要件になります。
- Bool を内部で保持する
- Bool の値に応じて、異なるラベルを表示する
- Bool の値が変更された際には、指定した closure 経由で変更後の値が取得できる
1つづつ 順番に実装していきます。
Bool を保持する View を定義
名前は、SDSStatefulButton としています。
内部に Bool を保持する Button を表示する View を定義します。
struct SDSStatefulButton : View {
// (1)
@State private var bValue: Bool
// (2)
public init(_ initialValue: Bool) {
self._bValue = State(wrappedValue: initialValue)
}
public var body: some View {
// (3)
Button(action: {
self.bValue.toggle()
}, label: {
Text("value: \(bValue ? "true" : "false" )")
})
}
}
- Bool を @State な変数として定義します
- 外部から初期値を指定できるようにしています
- Button を表示しています。bValue の値に応じて Button のラベルの Text を変更しています。
以下のような ContentView で動作を確認してみます。
struct ContentView: View {
var body: some View {
VStack {
SDSStatefulButton(true)
}
.padding()
}
}
以下のような動作になります。
true / false でラベルが切り替わる Button
Button の定義と同じように、表示に使う View を外部から 渡せるようにします。内部の Bool 値に応じて、異なる View を表示できるように、2つの View を設定できるようにします。
// (1)
public struct SDSStatefulButton<TrueLabel:View, FalseLabel:View> : View {
@State private var bValue: Bool
var trueLabel: TrueLabel
var falseLabel: FalseLabel
public init(_ initialValue: Bool,
@ViewBuilder trueLabel: () -> TrueLabel,
@ViewBuilder falseLabel: () -> FalseLabel) {
self._bValue = State(wrappedValue: initialValue)
// (2)
self.trueLabel = trueLabel()
self.falseLabel = falseLabel()
}
public var body: some View {
Button(action: {
self.bValue.toggle()
}, label: {
// (3)
if bValue {
trueLabel
} else {
falseLabel
}
})
}
}
- Generics を使用して、On/Off 用の View を指定します。
- 渡された View を内部に保持します
- bValue の値に応じて、適切なビューを表示します
ボタンを使う側は以下のように書くことができます。
struct ContentView: View {
@State private var bValue: Bool = true
var body: some View {
VStack {
SDSStatefulButton(true, trueLabel: {
// (1)
Image(systemName: "plus.app").resizable()
}, falseLabel: {
// (2)
Image(systemName: "minus.square").resizable()
})
.frame(width: 50, height: 50)
}
.padding()
}
}
- true 時に表示される Label (View) を外部から指定します
- false 時に表示される Label (View) を外部から指定します
以下のような動作になります。
ここまでで、3つの要件のうちの最初の2つを満たせました。
値変更時に closure を呼び出す
初期化時に渡された closure を呼び出すだけです。
//
// SDSStatefulButton.swift
//
// Created by : Tomoaki Yagishita on 2021/06/04
// © 2021 SmallDeskSoftware
//
import SwiftUI
public struct SDSStatefulButton<OnLabel:View, OffLabel:View> : View {
@State private var bValue: Bool
var trueLabel: OnLabel
var falseLabel: OffLabel
var onChange: ((Bool) -> Void)?
public init(_ initialValue: Bool,
@ViewBuilder onLabel: () -> OnLabel,
@ViewBuilder offLabel: () -> OffLabel,
onChange: ((Bool) -> Void)? = nil) {
self._bValue = State(wrappedValue: initialValue)
self.trueLabel = onLabel()
self.falseLabel = offLabel()
self.onChange = onChange
}
public var body: some View {
Button(action: {
self.bValue.toggle()
onChange?(self.bValue)
}, label: {
if bValue {
trueLabel
} else {
falseLabel
}
})
}
}
ボタンを使う側は以下のように書くことができます。
struct ContentView: View {
@State private var text: String = "value"
var body: some View {
VStack {
SDSStatefulButton(true, trueLabel: {
Image(systemName: "plus.app").resizable()
}, falseLabel: {
Image(systemName: "minus.square").resizable()
}, onChange: { value in
text = String("value is changed to \(value ? "true" : "false")")
})
.frame(width: 50, height: 50)
Text(text)
}
.padding()
}
}
以下のような動作になります。
これで、内部に Bool を保持し、値によって 表示を変更し、値変更時にはclosure が呼ばれる Button を作ることができました。
まとめ:Stateful な Button の作り方
- 変数は、@State で保持し、変更に応じてビューが更新されるようにする
- Generics を使って、さまざまな View を Label に使用できるようにする
- 外部から closure を渡せるようにして、値変更時には、closure を呼ぶ
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
SwiftUI 学習におすすめの本
SwiftUI 徹底入門
SwiftUI は、グラフィカルなライブラリということもあり、文字だけのテキストよりは、画像が多く入れられた書籍を読むと理解が進みやすいです。
自分で購入した中でおすすめできるものとしては、以下のものです。
2019 年発表の SwiftUI 1.0 相当を対象にしているので、それ以降に追加された機能は、説明されていません。
ですが、SwiftUI 入門書としては、非常によくできていますし、わかりやすいです。 この本で学習した後に、追加分を学習しても良いと思います。
SwiftUIViewsMastery
英語での説明になってしまいますが、以下の本もおすすめです。
1ページに、コードと画面が並んでいるので、非常にわかりやすいです。
View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。
超便利です
販売元のページは、こちらです。
Sponsor Link