結論
わからん。誰か詳しい人にどういう挙動が起きているのか教えてほしい。対応策を教えてもらえるともっと嬉しい。
書いてみたコード
画面にはTextEditorとその文字をクリアするButtonしかない非常にシンプルなもの。
import SwiftUI @_spi(Advanced) import SwiftUIIntrospect struct ContentView: View { @State var text: String = "" @Weak var textView: UITextView? { didSet { textView?.delegate = delegate } } var delegate = Delegate() final class Delegate: NSObject, UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { print("Delegate:", textView.text) } } var body: some View { VStack { TextEditor(text: $text) .onChange(of: text, { print(text) }) .introspect(.textEditor, on: .iOS(.v16, .v17)){ textView in self.textView = textView } Button { text = "" } label: { Text("文字クリア") } } .padding() } }
起きること
textView?.delegate = delegate
でDelegateを渡すとしっかりと func textViewDidChange(_ textView: UITextView)
が紐づいてそちらの処理は発火する。一方で、 .onChange(of: text)
とButton内の text = ""
は発火しなくなる。当然ながらtextView?.delegate = delegate
をコメントアウトしてやれば、.onChange(of: text)
も text = ""
によるクリアも正しく動作する。
TextEditorの裏側にはUITextViewがおり、そのUITextViewDelegateを奪ってしまうから発火しないのだろうという推測はできるのだが……。
super.
的なコードを書いてどちらも発火させることはできないんだろうか? UITextViewDelegateがつなぎ込めてしまえば実装上は実現できるけれど、なんかね。