焦点处理与字段串联
意图
使用@FocusState控制键盘焦点、串联字段,并在复杂表单中协调焦点。
核心模式
- 使用枚举表示可聚焦的字段。
- 在
onAppear中设置初始焦点。 - 使用
.onSubmit将焦点移动到下一个字段。 - 对于动态字段列表,使用带关联值的枚举(例如
.option(Int))。
示例:单字段焦点
structAddServerView:View{@Stateprivatevarserver=""@FocusStateprivatevarisServerFieldFocused:Boolvarbody:someView{Form{TextField("Server",text:$server).focused($isServerFieldFocused)}.onAppear{isServerFieldFocused=true}}}示例:用枚举串联焦点
structEditTagView:View{enumFocusField{casetitle,symbol,newTag}@FocusStateprivatevarfocusedField:FocusField?varbody:someView{Form{TextField("Title",text:$title).focused($focusedField,equals:.title).onSubmit{focusedField=.symbol}TextField("Symbol",text:$symbol).focused($focusedField,equals:.symbol).onSubmit{focusedField=.newTag}}.onAppear{focusedField=.title}}}示例:可变字段的动态焦点
structPollView:View{enumFocusField:Hashable{caseoption(Int)}@FocusStateprivatevarfocused:FocusField?@Stateprivatevaroptions:[String]=["",""]@StateprivatevarcurrentIndex=0varbody:someView{ForEach(options.indices,id:\.self){indexinTextField("Option\(index+1)",text:$options[index]).focused($focused,equals:.option(index)).onSubmit{addOption(at:index)}}.onAppear{focused=.option(0)}}privatefuncaddOption(at index:Int){options.append("")currentIndex=index+1DispatchQueue.main.asyncAfter(deadline:.now()+0.01){focused=.option(currentIndex)}}}应保留的设计选择
- 让焦点状态保持在拥有字段的视图本地。
- 用焦点变化驱动 UX(验证消息、辅助 UI)。
- 使用 ScrollView/Form 时,配合
.scrollDismissesKeyboard(...)。
陷阱
- 不要把焦点状态存储在共享对象中;它是视图局部的。
- 避免在动画期间激进地切换焦点;如有必要可延迟。