見出し画像

[SwiftUI] iOS16以下でもonChange(of:initial:_:)が使いたい!

概要

SwiftUIで値の変更を監視する便利なメソッドonChange
スピークバディプロジェクト内ではGeometoryReaderと併用して、子ビューのframeを取得したりするのに使用しています!

iOS16以前のonChangeではViewの初期表示時(値変動前)にonChangeのクロージャ内の処理が走ることはありませんでした。

なので、よくこんなふうにonAppearと併用する形でコードを書いていました。

onAppear { action(value) }
  .onChange(of: value, perform: action)

しかし、iOS17からonChangeinitialというプロパティが追加されたことにより、このプロパティをtrueにすると初回表示時にもクロージャが呼ばれるようになります!

しかしながら、スピークバディは未だiOS17未満もサポートしています…
便利なものが出たのにいつまでもonAppearとの併用なんて嫌だ!
ということで以下のextensionを定義してみました。

コード紹介

実装

extension View {
  @ViewBuilder func onChange<V: Equatable>(of value: V, initial: Bool, perform action: @escaping (_ newValue: V) -> Void) -> some View {
    if #available(iOS 17.0, *) {
      onChange(of: value, initial: initial) {
        action($1)
      }
    } else if initial {
      onAppear { action(value) }
        .onChange(of: value, perform: action)
    } else {
      onChange(of: value, perform: action)
    }
  }
}

使用例

EmptyView()
  .onChange(of: value, initial: true) { newValue in
    // 値変更に伴う処理
  }

余談

ちなみにiOS17以降のonChangeinitialプロパティの他にクロージャの引数としてoldValueも取れるようになっています。
このoldValueをiOS17未満でも使用したい場合は、以下のように書くと再現できるようです!

EmptyView()
  .onChange(of: value) { [value] newValue in
    let oldValue = value
    let newValue = newValue
  }

これもextension化しちゃえば気軽に使えますね!

参考記事

めちゃめちゃ勉強になりました…🙇‍♂️

おわり

お読みいただきありがとうございます!
もしなにかのお役に立つことがございましたら、
♡を頂けますとすごく励みになります😊

この記事が気に入ったらサポートをしてみませんか?