You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.01 03:02:51