I have a view with a @FocusState
that I want to pass into a subview. For the subview, I use @FocusState.Binding
. This works fine if I don't use a custom initializer ... but I can't figure out the syntax for how it works with a custom view initializer, which I need for other reasons.
Here's the code that works:
struct TestListButtonsAndListFocusView: View {
@FocusState var buttonFocusState: ButtonState?
@ObservedObject var viewModel: ViewModel = ViewModel()
var body: some View {
TestListView(viewModel: viewModel, bindedFocusButtonState: $buttonFocusState) // for custom init, replace with following line
//TestListView(viewModel: viewModel, focusButtonState: $buttonFocusState) // 1. Uncomment this for custom initializer
}
}
struct TestListView: View {
@State private var items1: [TimedItem] = [
TimedItem(number: 1, timestamp: "2024-11-20 10:00"),
TimedItem(number: 2, timestamp: "2024-11-20 11:00"),
TimedItem(number: 3, timestamp: "2024-11-20 12:00")
]
@ObservedObject var viewModel: ViewModel
@FocusState.Binding var bindedFocusButtonState: ButtonState?
@State var selectedItem: TimedItem.ID?
var body: some View {
List(items1, selection: $selectedItem) { item in
ContentListItemView(item: item)
}
.onChange(of: bindedFocusButtonState) {
if let bindedFocusButtonState {
print("TestListView - bindedListFocusState has changed to \(bindedFocusButtonState)")
if bindedFocusButtonState == .one && selectedItem == nil {
selectedItem = items1.first?.id
}
} else {
print("ListOneView - bindedListFocusState has changed to nil")
}
}
}
// 2. Uncomment this
/*init(viewModel: ViewModel, focusButtonState: FocusState<ButtonState?>.Binding) {
// how to pass in @FocusState.Binding?
self.viewModel = viewModel
self.bindedFocusButtonState = focusButtonState
// Error: Cannot assign value of type 'FocusState<ButtonState?>.Binding' to type 'ButtonState?'
}*/
}
public class ViewModel: NSObject, ObservableObject {
@Published public var selectedButton: ButtonState? = ButtonState.one
}
public enum ButtonState: Int, CaseIterable, Hashable, Identifiable {
public var id: Self {
return self
}
case one, two, three
}
#Preview {
TestListButtonsAndListFocusView()
}
However, if I uncomment out the line for the custom initializer, and then the line in TestListButtonsAndListFocusView
to use the custom initializer, the syntax is wrong and I get the error:
Error: Cannot assign value of type 'FocusState<ButtonState?>.Binding' to type 'ButtonState?'
I'm not sure how to then initialize the @FocusState.Binding
this way. I know it works if I use var bindedFocusButtonState: FocusState<ContactTabStyle?>.Binding
instead, and then use that in the initializer as well. But I'd really like to figure out how to use the new @FocusState.Binding
with the custom initializer, as it avoids having to access wrappedValue
and is easier to observe with onChange
You should assign to the underscore-prefixed property _bindedFocusButtonState
.
self._bindedFocusButtonState = focusButtonState
This is the property actually containing the FocusState.Binding
. bindedFocusButtonState
on the other hand, is actually a computed property that returns _bindedFocusButtonState.wrappedValue
.
In particular, the line
@FocusState.Binding var bindedFocusButtonState: ButtonState?
declares three properties:
private var _bindedFocusButtonState: FocusState<ButtonState?>.Binding
var bindedFocusButtonState: ButtonState? {
get { _bindedFocusButtonState.wrappedValue }
set { _bindedFocusButtonState.wrappedValue = newValue }
}
var $bindedFocusButtonState: FocusState<ButtonState?>.Binding {
get { _bindedFocusButtonState.projectedValue }
}