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

SwiftUI中ScrollView内的TextEditor输入长文本时被键盘遮挡的问题求助

SwiftUI中ScrollView内的TextEditor输入长文本时被键盘遮挡的问题求助

大家好,我现在在做一个SwiftUI的Demo,里面用ScrollView包裹了一组可编辑的多行文本框(基于TextEditor实现的自定义MultilineTextFieldView)。正常情况下UI表现都没问题,但只要其中一个文本框输入了大量内容(比如20-25行),键盘就会开始遮挡TextEditor的底部区域,我没法滚动到文本末尾去查看或编辑。

下面是我简化后的代码:

struct ContentView: View {
    @State private var textItems: [TextItem] = [
        TextItem(id: 1, text: "First text field"),
        TextItem(id: 2, text: "Second text field"),
        // 更多条目...
    ]
    @State private var keyboardHeight: CGFloat = 0
    
    var body: some View {
        NavigationView {
            ScrollViewReader { proxy in
                ScrollView {
                    LazyVStack(spacing: 16) {
                        ForEach($textItems) { $item in
                            MultilineTextFieldView(
                                text: $item.text,
                                placeholder: "Enter text for item \(item.id)...",
                                onEditingChanged: { isEditing in
                                    if isEditing {
                                        withAnimation {
                                            proxy.scrollTo(item.id, anchor: .center)
                                        }
                                    }
                                },
                                onTextChanged: {}
                            )
                            .id(item.id)
                        }
                    }
                    .padding()
                    .padding(.bottom, keyboardHeight > 0 ? keyboardHeight - 34 : 0)
                }
                .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { notification in
                    if let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
                        let keyboardRect = keyboardFrame.cgRectValue
                        withAnimation {
                            keyboardHeight = keyboardRect.height
                        }
                    }
                }
                .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
                    withAnimation {
                        keyboardHeight = 0
                    }
                }
            }
            .navigationTitle("Multiline Text Fields")
        }
    }
}

struct MultilineTextFieldView: View {
    @Binding var text: String
    let placeholder: String
    let onEditingChanged: (Bool) -> Void
    let onTextChanged: () -> Void
    
    @FocusState private var isFocused: Bool
    
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack {
                Text("Text Field")
                Spacer()
                Text("\(text.count) chars")
            }
            
            ZStack(alignment: .topLeading) {
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color(.systemGray6))
                    .stroke(isFocused ? Color.blue : Color.clear, lineWidth: 2)
                
                if text.isEmpty {
                    Text(placeholder)
                        .foregroundColor(.secondary)
                        .padding(.horizontal, 16)
                        .padding(.vertical, 12)
                        .allowsHitTesting(false)
                }
                
                TextEditor(text: $text)
                    .padding(.horizontal, 12)
                    .padding(.vertical, 8)
                    .background(Color.clear)
                    .focused($isFocused)
                    .onChange(of: isFocused, perform: onEditingChanged)
                    .onChange(of: text, perform: { _ in
                        onTextChanged()
                    })
                    .fixedSize(horizontal: false, vertical: true)
            }
            .frame(minHeight: 40)
        }
    }
}

struct TextItem: Identifiable {
    let id: Int
    var text: String
}

我已经尝试过的解决方法:

  • 给TextEditor添加.fixedSize(horizontal: false, vertical: true)修饰符
  • 监听键盘高度变化,给ScrollView的内容动态添加底部内边距
  • 借助ScrollViewReader,在文本框开始编辑时自动滚动到当前条目(锚点设为.center)
  • 通过NotificationCenter监听键盘的显示/隐藏通知来更新键盘高度

但问题依然存在:当文本框内容过长时,键盘会遮挡文本框底部,我无法滚动到文本末尾进行编辑。

我期望的效果是:当前处于编辑状态的TextEditor(哪怕内容很长)能自动保持在键盘上方完全可见,不会被遮挡,我可以正常滚动编辑全部文本内容。

补充说明:我试过用UIKit的UITextView通过UIViewRepresentable来实现相同功能,是可以正常工作的,但我想尽量用SwiftUI原生的TextEditor来解决这个问题。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 12:13:07