You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
All the examples of @FocusState that I can find all treat focus as if it only exists in a single view. I've been trying to work out how to do this in macOS where there can be dozens of different focusable views.
You could have a single enum that holds every possible focusable item in the app. In this approach, only the root view would maintain a @FocusState property, and you'd bind this to a notional Feature.State.focus property in the reducer's state. Then you'd have to pass a binding to this focus state to the entire hierarchy of views to reach the focusable leaves. This would be pretty unwieldy and require a lot of knowledge about child features at the top level, but it would work.
Ideally, each feature would track focus for its own views in the state. Unfortunately, I don't see how to make this happen. Here's a strawman to demonstrate the issues. You can grab this demo project at https://github.com/seanmrich/Focus to check it out. First, here's a parent feature that tracks a single text field and a child feature.
structFeature:ReducerProtocol{enumFocus:Hashable{case featureField
case child(Child.Focus)}structState:Equatable{varfeatureText:String=""varchildText1:String=""varchildText2:String=""varfocus:Focus?=.featureField
}enumAction{case textChanged(String)case focusChanged(Focus?)case child(Child.Action)}varbody:someReducerProtocol<State,Action>{Scope(state: \.childState, action:/Action.child){Child()}Reduce{ state, action in
switch action {caselet.textChanged(new):
state.featureText = new
return.none
caselet.focusChanged(new):
state.focus = new
return.none
case.child:return.none
}}}}extensionFeature.State{varchildState:Child.State{get{.init(
text1: childText1,
text2: childText2,
focus:(/Feature.Focus.child).extract(from: focus))}set{
childText1 = newValue.text1
childText2 = newValue.text2
if let childFocus = newValue.focus {
focus =.child(childFocus)}}}}
Now here's the child feature that tracks its own two fields:
structChild:ReducerProtocol{enumFocus:Hashable{case field1
case field2
}structState:Equatable{vartext1:String=""vartext2:String=""varfocus:Focus?=nil}enumAction{case text1Changed(String)case text2Changed(String)case focusChanged(Focus?)}func reduce(into state:inoutState, action:Action)->EffectTask<Action>{
switch action {caselet.text1Changed(new):
state.text1 = new
return.none
caselet.text2Changed(new):
state.text2 = new
return.none
caselet.focusChanged(new):
state.focus = new
return.none
}}}
Finally, here's both views. Notice that each one tries to bind its focus state to the model:
As you would expect, there's a collision when the system sets the focus for each view one at a time. The view whose focus is removed always comes last so Feature.State.focus will always be reset to nil. This obviously won't work, but I don't see how you can break down nested focus states to avoid the single top-level enum. I'd greatly appreciate any advice.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
All the examples of
@FocusState
that I can find all treat focus as if it only exists in a single view. I've been trying to work out how to do this in macOS where there can be dozens of different focusable views.You could have a single enum that holds every possible focusable item in the app. In this approach, only the root view would maintain a
@FocusState
property, and you'd bind this to a notionalFeature.State.focus
property in the reducer's state. Then you'd have to pass a binding to this focus state to the entire hierarchy of views to reach the focusable leaves. This would be pretty unwieldy and require a lot of knowledge about child features at the top level, but it would work.Ideally, each feature would track focus for its own views in the state. Unfortunately, I don't see how to make this happen. Here's a strawman to demonstrate the issues. You can grab this demo project at https://github.com/seanmrich/Focus to check it out. First, here's a parent feature that tracks a single text field and a child feature.
Now here's the child feature that tracks its own two fields:
Finally, here's both views. Notice that each one tries to bind its focus state to the model:
As you would expect, there's a collision when the system sets the focus for each view one at a time. The view whose focus is removed always comes last so
Feature.State.focus
will always be reset tonil
. This obviously won't work, but I don't see how you can break down nested focus states to avoid the single top-level enum. I'd greatly appreciate any advice.Beta Was this translation helpful? Give feedback.
All reactions