文字っぽいの。

文字を書いています。写真も混ざります。

`.safeAreaBar` を設定すると SwiftUI.TextEditorの `.focused` が動かなくなる。

環境

起きること

まずは動くコード

import SwiftUI

struct ContentView: View {
    @State var text: String = ""
    @FocusState var focus: Bool

    var body: some View {
        TextEditor(text: $text)
            .focused($focus)
            .onChange(of: focus) { oldValue, newValue in
                print(oldValue, newValue) // => (false, true)
            }
    }
}

当然ながらこのコードは動く。TextEditorをタップしてカーソルが表示されれば、focusが変更されてprintされる。

次に動かなくなるコード

import SwiftUI

struct ContentView: View {
    @State var text: String = ""
    @FocusState var focus: Bool

    var body: some View {
        TextEditor(text: $text)
            .focused($focus)
            .onChange(of: focus) { oldValue, newValue in
                print(oldValue, newValue) // => Not work
            }
            // ↓追加
            .safeAreaBar(edge: .bottom) {
                HStack {
                    Image(systemName: "globe")
                }
            }
    }
}

こうするとprintされないだけでなく、

Button("Close Keyboard") {
    focus = false
}

といった、@FocusState経由でキーボードを閉じる処理も動かなくなる。.onChange が呼ばれないわけではなく、内部的にBindingされていなさそう。

対策

@FocusState が生まれる前の古き良き手法を使うしか無い。

キーボードを閉じるには、

UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)

キーボードの表示非表示でハンドリングしたいなら

.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { _ in
    // キーボード表示時
}
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
    // キーボード非表示時
}

を使うことでなんとか迂回は可能。

感想

そもそも .safeAreaBariOS 26.0+のため、使えるアプリはかなり少なくハマることは少なそう。

同じ現象が自分の環境だけかと思って調べてみたけど、Redditでも同じ現象を話している人が居た。

ウキウキで最新APIを使って「これめっちゃ便利じゃーん」ってしてたら無駄に時間が消費されて悲しい。

さいごに

Apple Feedback Assistant経由でバグ報告はしておきました。