SwiftUI中TabView切换后其他视图FocusState键盘Done按钮不显示
SwiftUI TabView子视图键盘Done按钮不显示问题
问题详情
开发了一个包含三个子视图的TabView页面,聚焦第一个视图的TextField时,键盘上方正常显示Done按钮;但切换到第二、第三个视图后,聚焦其内部TextField时,键盘上方没有出现Done按钮。每个视图都单独设置了@FocusState属性,但问题仍未解决。
原代码示例
import SwiftUI struct TabViewTest: View { var body: some View { TabView { ViewOne() .tabItem { Image(systemName: "1.circle") } ViewTwo() .tabItem { Image(systemName: "2.circle") } ViewThree() .tabItem { Image(systemName: "3.circle") } } } } #Preview { TabViewTest() } import SwiftUI struct ViewOne: View { @State private var text: String = "" @FocusState private var isFocused: Bool var body: some View { NavigationStack { VStack { Text("Text 1") TextField("Text 1", text: $text) .focused($isFocused) } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { isFocused = false } } } } } } #Preview { ViewOne() } import SwiftUI struct ViewTwo: View { @State private var text: String = "" @FocusState private var isFocused: Bool var body: some View { NavigationStack { VStack { Text("Text 2") TextField("Text 2", text: $text) .focused($isFocused) } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { isFocused = false } } } } } } #Preview { ViewTwo() }
问题原因
这是SwiftUI中TabView与NavigationStack嵌套的常见问题:TabView默认会延迟加载非当前选中的子视图,当切换到第二/第三个视图时,其内部独立的NavigationStack没有正确关联到当前窗口的场景上下文,导致键盘工具栏无法被系统正确渲染。
解决方案
方案1:将NavigationStack提升到TabView外层(推荐)
把所有子视图的NavigationStack移到TabView外面,让整个TabView共享一个全局导航栈,这样键盘工具栏能稳定绑定到正确的上下文。
修改后代码:
import SwiftUI struct TabViewTest: View { var body: some View { // 全局NavigationStack包裹TabView NavigationStack { TabView { ViewOne() .tabItem { Image(systemName: "1.circle") } ViewTwo() .tabItem { Image(systemName: "2.circle") } ViewThree() .tabItem { Image(systemName: "3.circle") } } } } } // 子视图移除内部的NavigationStack struct ViewOne: View { @State private var text: String = "" @FocusState private var isFocused: Bool var body: some View { VStack { Text("Text 1") TextField("Text 1", text: $text) .focused($isFocused) } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { isFocused = false } } } } } struct ViewTwo: View { @State private var text: String = "" @FocusState private var isFocused: Bool var body: some View { VStack { Text("Text 2") TextField("Text 2", text: $text) .focused($isFocused) } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { isFocused = false } } } } } struct ViewThree: View { @State private var text: String = "" @FocusState private var isFocused: Bool var body: some View { VStack { Text("Text 3") TextField("Text 3", text: $text) .focused($isFocused) } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { isFocused = false } } } } } #Preview { TabViewTest() }
方案2:强制子视图切换时重建(不推荐,影响性能)
如果必须保留每个子视图的独立NavigationStack,可以通过绑定TabView的选中状态,给子视图添加.id()强制切换时重建视图,确保NavigationStack重新关联上下文。
修改后代码:
import SwiftUI struct TabViewTest: View { // 绑定Tab选中状态 @State private var selectedTab = 0 var body: some View { TabView(selection: $selectedTab) { ViewOne() .tabItem { Image(systemName: "1.circle") } .tag(0) ViewTwo() .tabItem { Image(systemName: "2.circle") } .tag(1) // 用选中状态作为id,切换时强制重建视图 .id(selectedTab) ViewThree() .tabItem { Image(systemName: "3.circle") } .tag(2) .id(selectedTab) } } } // 子视图保留原有结构不变 struct ViewOne: View { @State private var text: String = "" @FocusState private var isFocused: Bool var body: some View { NavigationStack { VStack { Text("Text 1") TextField("Text 1", text: $text) .focused($isFocused) } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { isFocused = false } } } } } } struct ViewTwo: View { @State private var text: String = "" @FocusState private var isFocused: Bool var body: some View { NavigationStack { VStack { Text("Text 2") TextField("Text 2", text: $text) .focused($isFocused) } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { isFocused = false } } } } } } struct ViewThree: View { @State private var text: String = "" @FocusState private var isFocused: Bool var body: some View { NavigationStack { VStack { Text("Text 3") TextField("Text 3", text: $text) .focused($isFocused) } .toolbar { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { isFocused = false } } } } } }
注意:方案2会导致Tab切换时子视图完全重建,可能丢失临时状态,且影响性能,仅在无法调整NavigationStack结构时使用。
内容的提问来源于stack exchange,提问作者user19624355
相关产品推荐
相关产品推荐

