Sponsor Link
環境&対象
- macOS14.3
- Xcode 15.2
- iOS 17.2
- Swift 5.9
onChange
onChange の基本的な使い方は、以下の記事で説明しています。
複数の変数を監視したい背景
SwiftUI でアプリを作っていると、いくつかの変数のいずれかが変更された時に、特定の処理を行わせたいというケースが出てくることがあります。
もちろん、onChange を複数 使って記述することもできます。以下のようになります。
import SwiftUI
struct ContentView: View {
@State private var value1: Int = 3
@State private var value2: Double = 3.4
var body: some View {
VStack {
Button(action: {
value1 += 1
}, label: { Text("+1 at value1") })
Button(action: {
value2 += 0.4
}, label: { Text("+0.4 at value2") })
}
.padding()
.onChange(of: value1, { oldValue, newValue in
// some process
})
.onChange(of: value2, { oldValue, newValue in
// some process
})
}
}
同じ処理を、それぞれの onChange に書いても良いですし、処理を行うメソッドを作成して、双方の onChange から呼び出すのもOKです。
ですが、なんとなく、まとめて書きたくなります。
上記のままだと、
・異なることをきっかけに、異なる処理が 行われている
ように見えるかもしれません。
「何をきっかけに処理を行うのか」「どのような処理をおこなうのか」をどのようにコードに反映するかは、コードが読みやすくなるポイントだと思います。
このケースでは、”いずれのきっかけでも同じ処理を行いたい” ということを明示的にコードにしたいというケースです。
具体的には、同じ処理を (まとめた) 1つの onChange から呼び出すことで、読みやすくなることを期待しています。
複数の変数を監視する onChange
いくつかのケースに分けて説明していきます。
同じデータ型を onChange で監視する
同じ型の複数変数を監視する場合から、考えます。
解決策は、ローカルに配列を作り、その配列を onChange で監視する です。
import SwiftUI
struct ContentView: View {
@State private var value1: Int = 3
@State private var value2: Int = 5
var body: some View {
VStack {
Button(action: {
value1 += 1
}, label: { Text("+1 at value1") })
Button(action: {
value2 += 2
}, label: { Text("+2 at value2") })
}
.padding()
// 配列にまとめて監視するパターン
.onChange(of: [value1, value2]) { oldValue, newValue in
// some process
}
// // 変数ごとに監視するパターン
// .onChange(of: value1, { oldValue, newValue in
// // some process
// })
// .onChange(of: value2, { oldValue, newValue in
// // some process
// })
}
}
onChange を変数ごとに書いても、onChange で配列を受けても 結果としては同じ動作をします。
異なるデータ型 を onChange で監視する
同じデータ型では、配列に対して onChange することで、その変更を監視できました。
異なるデータ型についても、同じように 配列にしようとすると、エラーになります。
import SwiftUI
struct ContentView: View {
@State private var value1: Int = 3
@State private var value2: Double = 5.2
var body: some View {
VStack {
Button(action: {
value1 += 1
}, label: { Text("+1 at value1") })
Button(action: {
value2 += 2.1
}, label: { Text("+2.1 at value2") })
}
.padding()
// ERROR!!: Conflicting arguments to generic parameter 'Element' ('Int' vs. 'Double')
.onChange(of: [value1, value2]) { oldValue, newValue in
// some process
}
}
}
Value-type な Collection を作ると 基本的に 1つの型の Collection になります。
解決策の1つは Reference-type な要素を対象にすることですが、onChange で監視できなくなりますし、Value-type / Reference-type を安易な理由で切り替えるのはお勧めできません。
(Reference-type に切り替え、基底型の Collection にすることで、1つの Collection に収めることはできますが、onChange で監視することはできません。)
別の解決策は、共通の型に変換して配列にすることです。
以下では、Int, Double のどちらの型も String に変換して配列にしています。
import SwiftUI
struct ContentView: View {
@State private var value1: Int = 3
@State private var value2: Double = 5.2
var body: some View {
VStack {
Button(action: {
value1 += 1
}, label: { Text("+1 at value1") })
Button(action: {
value2 += 2.1
}, label: { Text("+2.1 at value2") })
}
.padding()
.onChange(of: [String(value1), String(value2)]) { oldValue, newValue in
// some process
}
}
}
上記の様にすることで、型の異なる複数の Value-type の変数を 1つの onChange で監視することができるようになります。
まとめ
複数の Value-type な変数を 1つの onChange で監視する方法を紹介しました。
- 同一型の変数であれば、配列に対して onChange を使用して監視する
- 異なる型の変数であれば、1つの方に変換した配列に対して onChange を使用して監視する
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
SwiftUI おすすめ本
SwiftUI を理解するには、以下の本がおすすめです。
SwiftUI ViewMatery
SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。
英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。
View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。
超便利です
販売元のページは、こちらです。
SwiftUI 徹底入門
# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。
Swift学習におすすめの本
詳解Swift
Swift の学習には、詳解 Swift という書籍が、おすすめです。
著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。
最新版を購入するのがおすすめです。
現時点では、上記の Swift 5 に対応した第5版が最新版です。
Sponsor Link