SwiftUI:键盘弹出时如何将List中的TextEditor上移
我完全懂你的困扰!SwiftUI里List嵌套TextEditor时,键盘自动上移的问题确实比TextField棘手很多,毕竟List的内部布局逻辑和普通容器不太一样。这里有两个实用的解决方案,你可以根据需求选择:
方案一:用ScrollView + LazyVStack替代List(推荐)
如果你的List不需要侧滑删除、分组等List专属特性,换成ScrollView+LazyVStack是最省心的方式,它能让TextEditor像TextField一样正常响应键盘,还能通过监听键盘通知做更精准的适配:
struct ContentView: View { @State var items: [String] = ["Apples", "Oranges", "Bananas", "Pears", "Mangos", "Grapefruit","Apples", "Oranges", "Bananas", "Pears", "Mangos", "Grapefruit"] @State private var newItemText : String = "" // 用于存储键盘高度 @State private var keyboardHeight: CGFloat = 0 var body: some View { ZStack(alignment: .bottom) { ScrollReader { proxy in ScrollView { LazyVStack(spacing: 16) { ForEach(items, id: \.self) { item in Text(item) .padding(.vertical, 8) } TextEditor(text: $newItemText) .frame(height: 100) .background(Color.gray.opacity(0.1)) .cornerRadius(8) .padding(.horizontal) .id("textEditor") // 标记ID,方便滚动定位 } .padding(.bottom, keyboardHeight) // 底部预留键盘空间 } .onAppear { // 监听键盘弹出/收起通知 NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { notification in guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } keyboardHeight = keyboardFrame.height // 动画滚动到TextEditor,确保它不被键盘遮挡 withAnimation { proxy.scrollTo("textEditor", anchor: .bottom) } } NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { _ in keyboardHeight = 0 } } .onDisappear { // 记得移除通知监听,避免内存泄漏 NotificationCenter.default.removeObserver(self) } } } } }
方案说明:
- ScrollView+LazyVStack的组合和List的视觉效果接近,同时避开了List对TextEditor的特殊布局限制
- 通过
ScrollReader可以精准滚动到TextEditor位置,配合底部内边距的动态调整,完美解决遮挡问题 - 用
LazyVStack可以保持List的懒加载特性,性能不受影响
方案二:保留List,手动适配键盘
如果你必须保留List(比如需要侧滑删除、分组等功能),可以通过监听键盘高度调整List的内边距,再手动触发滚动来让TextEditor显示在键盘上方:
struct ContentView: View { @State var items: [String] = ["Apples", "Oranges", "Bananas", "Pears", "Mangos", "Grapefruit","Apples", "Oranges", "Bananas", "Pears", "Mangos", "Grapefruit"] @State private var newItemText : String = "" @State private var keyboardHeight: CGFloat = 0 @State private var isTextEditorFocused = false var body: some View { ZStack(alignment: .bottom) { List{ ForEach(items, id: \.self) { item in Text(item) } TextEditor(text: $newItemText) .frame(height: 100) .background(Color.gray.opacity(0.1)) .cornerRadius(8) .onTapGesture { isTextEditorFocused = true } } .contentInset(.bottom, keyboardHeight) // 动态调整List底部内边距 .onAppear { NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, object: nil, queue: .main) { notification in guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } keyboardHeight = keyboardFrame.height // 延迟触发滚动,确保键盘完全弹出后再调整位置 if isTextEditorFocused { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // 获取List底层的UIScrollView,手动滚动到底部 if let scrollView = UIApplication.shared.windows.first?.rootViewController?.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView { scrollView.scrollRectToVisible(CGRect(x: 0, y: scrollView.contentSize.height, width: 1, height: 1), animated: true) } } } } NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillHideNotification, object: nil, queue: .main) { _ in keyboardHeight = 0 isTextEditorFocused = false } } .onDisappear { NotificationCenter.default.removeObserver(self) } } } }
方案说明:
- 利用
contentInset给List底部增加和键盘高度一致的空间,避免内容被遮挡 - 通过监听TextEditor的点击状态,在键盘弹出后手动操作底层的UIScrollView滚动到底部,确保TextEditor可见
- 注意添加延迟,因为键盘弹出需要时间,直接滚动可能会位置不准
内容的提问来源于stack exchange,提问作者unknown
相关产品推荐
相关产品推荐

