Sponsor Link
環境&対象
- macOS Monterey 13 Beta9
- Xcode 14.1 beta 3
- iOS 16.0
FocusedValue/FocusedBindingの使い所
FocusState は、ビューがフォーカスされているかどうかを そのビュー中で 参照することができました。
FocusedValue / FocusedBinding は、もうすこし拡張された機能です。
ビューがフォーカスされたことをきっかけに、特定のデータを設定することができ、参照することができるようになります。
データの使われ方に応じて、FocusedValue, FocusedBinding が用意されています。
使用シーンとしては、以下のようなシーンが考えられます。
・フォーカスされているビューに応じて、切り替えられるメニュー/コマンド (Apple ドキュメントによく出てきます)
・フォーカスされているテキストフィールドに応じて、内容が切り替えて表示される補助ビュー
この記事では、FocusedBinding の使い方を説明していきます。
FocusState は、以下の記事で説明しています。
[SwiftUI] @FocusState の使い方
FocusedValue は、以下の記事で説明しています。
[SwiftUI] FocusedValue の使い方
FocusedBinding
FocusedValue は、フォーカスを受けたタイミングでデータをセットし、別の箇所から参照することができるものでした。
ただし、別の箇所からは参照のみで データ変更して返すことはできませんでした。
別の箇所からもデータを変更することができるようにするのが、FocusBinding です。
FocusedValue でアクセスする変数の定義
FocusedBinding を使用するためには FocusedValue のときと同様に設定する/アクセスされるためのデータを定義することが必要です。
FocusedBinding のデータも、内部的には、キーを使用して管理されますので、まずは、キーを定義することが必要です。
以下のように、FocusedValueKey に準拠する形で定義します。以下の例では、MyFocusedBindingDataKey というキーが定義できます。
扱う型を typealias を使って、Value に設定します。以下の例では Binding<Int> 型です。
struct MyFocusedBindingDataKey: FocusedValueKey {
//typealias Value = Int
typealias Value = Binding<Int>
}
次に、キーを使ってアクセスされた時の処理を定義します。FocusedValues の extension として定義します。
extension FocusedValues {
var myIntDataBinding: MyFocusedBindingDataKey.Value? {
get { self[MyFocusedBindingDataKey.self] }
set { self[MyFocusedBindingDataKey.self] = newValue }
}
}
キーの定義や FocusedValues の extension での追加コードは、ほとんど FocusedValue と同じです。
異なる点は、データ型が、Binding であるということです。
データ型が Binding であっても、フォーカスされていないケースを処理するために、FocusedValues でのデータ型は Optional として 定義します。
値設定の仕方
FocusedBinding で扱われる値を設定するときにも、 FocusedValue と同じ View Modifier を使用します。
.focusedValue です。
func focusedValue(
_ keyPath: WritableKeyPath,
_ value: Value
) -> some View
Apple のドキュメントは、こちら。
以下のような形で、値を設定することになりますが、Binding 型を設定することが必要です。
@State private var myIntData1 = 1
~ snip ~
TextField("Title", $title)
.focusedValue(\.myIntDataBinding, $myIntData1)
FocusValue と同様に、FocusedBinding も ”ビューが フォーカスを受けたとき”に 値が設定される という点に注意することが必要です。
つまりフォーカスを受けなければ、値が設定されることはありません。
アクセス方法
設定された FocusedBinding にアクセスするには、Property Wrapper である @FocusedBinding を使います。
データ型が Optional であるため、確認してから 使用しています。
struct SubView: View {
@FocusedBinding(\.myIntDataBinding) var myBinding
var body: some View {
GroupBox("SubView") {
Button(action: {
if let value = myBinding {
myBinding = value * 8
}
}, label: {
Text("set x 8")
})
.disabled(myBinding == nil)
}
}
}
FocusedBinding のデータは、フォーカスされたビューが設定することによって設定されるため、まだフォーカスされていない状態であれば、nil となっています。つまり、Binding 経由でのデータを変更することはできません。上記コードでは、nil の時には、Button を disable にしています。
実装例
以下のようなアプリを作って動作を確認しました。
TextField 3つと Button を含む SubView で構成されています。
動画で動作を確認してもらうと、以下の点を確認することができます。
- TextField がフォーカスを受けると、それぞれの focusedValue で設定されている 1, 2, 3 がスクリーン下部に表示される。
- Button を押して、focusedValue を8倍にセットされる
- セットされた値は、記憶されていて、再度フォーカスされると、記録されている値が表示される
- 下位ビューでない、SubView が 受け取れていることからも 親子関係の無いビューからでも FocusedValue は、受け取ることができる
以下は使用したコードです。
//
// ContentView.swift
//
// Created by : Tomoaki Yagishita on 2022/09/30
// © 2022 SmallDeskSoftware
//
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
FieldsView()
SubView()
}
.padding()
}
}
struct FieldsView: View {
@FocusedBinding(\.myIntDataBinding) var intValue
@State private var text1 = ""
@State private var text2 = ""
@State private var text3 = ""
@State private var myIntData1 = 1
@State private var myIntData2 = 2
@State private var myIntData3 = 3
var body: some View {
Group {
TextField("Text1", text: $text1)
.focusedValue(\.myIntDataBinding, $myIntData1)
TextField("Text2", text: $text2)
.focusedValue(\.myIntDataBinding, $myIntData2)
TextField("Text3", text: $text3)
.focusedValue(\.myIntDataBinding, $myIntData3)
if let intValue = intValue {
Text("Current FocusedValue: \(intValue)")
} else {
Text("No value")
}
}.padding()
}
}
struct SubView: View {
@FocusedBinding(\.myIntDataBinding) var myBinding
var body: some View {
GroupBox("SubView") {
Button(action: {
if let value = myBinding {
myBinding = value * 8
}
}, label: {
Text("set x 8")
})
.disabled(myBinding == nil)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct MyFocusedBindingDataKey: FocusedValueKey {
typealias Value = Binding<Int>
}
extension FocusedValues {
var myIntDataBinding: MyFocusedBindingDataKey.Value? {
get { self[MyFocusedBindingDataKey.self] }
set { self[MyFocusedBindingDataKey.self] = newValue }
}
}
まとめ
FocusedBinding の使い方を説明しました。
- FocusedBinding は、FocusValue と似ている
- FocusedBinding は、Binding を対象とする
- FocusedValueKey に準拠したキーを定義する, Value に型も設定する
- FocusedValues の extension にアクセスを定義する
- focusedValue は、View がフォーカスを受けないと値をセットしない
- focusedValue は、フォーカスを失うと nil になる
説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。
SwiftUI おすすめ本
SwiftUI を理解するには、以下の本がおすすめです。
SwiftUI ViewMastery
SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。
英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。
View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。
超便利です
販売元のページは、こちらです。
SwiftUI 徹底入門
# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。
Swift学習におすすめの本
詳解Swift
Swift の学習には、詳解 Swift という書籍が、おすすめです。
著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。
最新版を購入するのがおすすめです。
現時点では、上記の Swift 5 に対応した第5版が最新版です。
Sponsor Link
末尾のコード例で「」が抜けてる気がします
> struct MyFocusedBindingDataKey: FocusedValueKey {
> typealias Value = Binding
> }
指摘ありがとうございます。
type parameter の < をエスケープし忘れて意味不明になっていました。