【SwiftUI】onChangeがiOS17で変更前の値(oldValue)が使い易く

SwiftUI

変更内容

iOS17以降でonChangeに追加が入りました。

public func onChange<V>(of value: V, initial: Bool = false, _ action: @escaping (_ oldValue: V, _ newValue: V) -> Void) -> some View where V : Equatable

要はonChangeに与えるクロージャの引数が2つになったバージョンが増えました。
以前のものはDeprecatedになったので以降はこちらを使うことが推奨されます。

クロージャに引数に変化前後の値の両方が与えられる様になりました。
つまり以下の様に変更になります。

//Deprecated
.onChange(of:changeValue) { newValue in        
    
}

//iOS17~
.onChange(of:changeValue) { oldValue, newValue in        
    
}

実例

次は実際の利用例を紹介します。
ScenePhase辺りでよく使うと思いますのでこれを使います。

//変数宣言。Viewは略
@Environment(\.scenePhase) private var scenePhase

//iOS16まで
.onChange(of: scenePhase) { newValue in
    if newValue == .inactive {
    //active to inactive (sceneWillResignActive)
    //and
    //background to inactive (sceneWillEnterForeground)
    }

    if scenePhase == .active && newValue == .inactive {
    //Only
    //active to inactive (sceneWillResignActive)
    }
}

//iOS17~
.onChange(of: scenePhase) { oldValue, newValue in
    if oldValue == .active && newValue == .inactive {
    //Only
    //active to inactive (sceneWillResignActive)
    }
}

ScenePhaseではinactiveを扱う時に変更前後の両方の値を見ないとSceneDelegateと同じ挙動ができません。

こういった場合に、以前のonChangeではoldValueを使う時はonChangeに渡したメンバ変数をそのまま使う必要がありました。
これでは直感的ではなく、仕様を把握していない人はメンバ変数の方も変わってしまっていると勘違いし、oldValue用に別途メンバ変数を用意した事がある人も居るのではないでしょうか?

iOS17以降はクロージャの引数として明確にoldValueがあるので直感的でわかりやすく、誰でも間違う事なく使えると思います。
コードの見通しもよくなりますね。

所感

今回は特に癖はなく単純に使い易くなった良い変更だと思います。
iOS17以降でしか使えないので既存プロジェクトでは使い難いと思いますが、新規プロジェクトや新機能の実装、互換を切って良い場合などでiOS17以降のみであれば積極的に使っていきたいですね。

コメント

タイトルとURLをコピーしました