[SwiftUI] 複数の変数を1つの onChange で監視する

SwiftUI2021

     
⌛️ 2 min.

onChange で複数の Value-type な変数を監視する方法を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS14.3
  • Xcode 15.2
  • iOS 17.2
  • Swift 5.9

onChange

onChange の基本的な使い方は、以下の記事で説明しています。

SwiftUI2021 [SwiftUI] onChange まとめ@2023

複数の変数を監視したい背景

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 で監視する方法を紹介しました。

複数の Value-type な変数を 1つの onChange で監視する方法
  • 同一型の変数であれば、配列に対して onChange を使用して監視する
  • 異なる型の変数であれば、1つの方に変換した配列に対して onChange を使用して監視する

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

SwiftUI おすすめ本

SwiftUI を理解するには、以下の本がおすすめです。

SwiftUI ViewMatery

SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。

英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。

View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。

超便利です

SwiftUIViewsMastery

販売元のページは、こちらです。

SwiftUI 徹底入門

# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。

Swift学習におすすめの本

詳解Swift

Swift の学習には、詳解 Swift という書籍が、おすすめです。

著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。

最新版を購入するのがおすすめです。

現時点では、上記の Swift 5 に対応した第5版が最新版です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です